Compare commits

...

1940 Commits

Author SHA1 Message Date
Blacksmith
4049d23562 [maven-release-plugin] prepare release forge-1.6.35 2020-06-29 02:47:40 +00:00
Blacksmith
50e2cf1f88 Update README.txt for release 2020-06-29 02:44:57 +00:00
swordshine
7f5a56fe4f Merge branch 'migrate-m21' into 'master'
Migrate m21 cards

See merge request core-developers/forge!2929
2020-06-29 01:40:00 +00:00
friarsol
bf74bd2f09 Update announcements 2020-06-28 21:29:42 -04:00
Sol
8f20aa39ca Merge branch 'm21' into 'master'
Updated deck legality

See merge request core-developers/forge!2925
2020-06-29 01:23:30 +00:00
friarsol
922c1fb541 Update block data for M21 2020-06-28 21:06:53 -04:00
friarsol
55973e8c75 Migrate new cards 2020-06-28 21:00:39 -04:00
swordshine
54e8c6f0ec Merge branch 'patch' into 'master'
Add Ormos, Archive Keeper

See merge request core-developers/forge!2927
2020-06-29 01:00:15 +00:00
friarsol
0837e0de8c Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2020-06-28 20:57:59 -04:00
swordshine
da6188e61c Update es-ES 2020-06-29 08:54:14 +08:00
Michael Kamensky
52fef4b724 Merge branch 'heartPiecerTrigger' into 'master'
ImmediateTrigger: use this trigger for heart-piercer manticore and get rid of CauseParam

See merge request core-developers/forge!2928
2020-06-28 16:20:09 +00:00
Hans Mackowiak
f29cd22926 ImmediateTrigger: use this trigger for heart-piercer manticore and get rid of CauseParam 2020-06-28 17:53:47 +02:00
swordshine
ab6a322f98 Fix a line in AiCostDecision 2020-06-28 22:35:32 +08:00
swordshine
e44125ec1c Update zh-CN 2020-06-28 21:53:51 +08:00
swordshine
558fbd3946 Add Ormos, Archive Keeper 2020-06-28 20:40:56 +08:00
austinio7116
1051fc676b Some Theros data added
(cherry picked from commit 0d48d99)
2020-06-28 13:05:41 +01:00
austinio7116
23737aa21a Updated deck legality
(cherry picked from commit c2f19b0)
2020-06-28 10:12:34 +01:00
swordshine
47f527b3ff Merge branch 'patch' into 'master'
Add Towering Titan

See merge request core-developers/forge!2924
2020-06-28 07:07:08 +00:00
swordshine
b06add33d2 Update the script 2020-06-28 14:44:17 +08:00
swordshine
24d2c8dc2f Add Towering Titan 2020-06-28 14:06:42 +08:00
swordshine
2fb6849ae2 Merge branch 'patch' into 'master'
Add Inniaz, the Gale Force

See merge request core-developers/forge!2921
2020-06-28 01:44:50 +00:00
swordshine
f5de946830 Merge branch 'mill2' into 'master'
Remove Mill effect from non-mill cards

See merge request core-developers/forge!2908
2020-06-28 01:44:43 +00:00
Tim Mocny
39deffdc56 Remove Mill effect from non-mill cards 2020-06-28 01:44:43 +00:00
swordshine
85062cba83 Merge branch 'JMP.27' into 'master'
JMP - 27 June

See merge request core-developers/forge!2922
2020-06-28 01:36:00 +00:00
Tim Mocny
17392a29ab JMP - 27 June 2020-06-28 01:35:59 +00:00
swordshine
aa21883049 Add Inniaz, the Gale Force 2020-06-27 22:28:20 +08:00
Michael Kamensky
ca7691adee Merge branch 'sanctumOfAll' into 'master'
M21: add Sanctum of All

Closes #1471

See merge request core-developers/forge!2920
2020-06-27 13:18:25 +00:00
Hans Mackowiak
51fdf21d1e M21: add Sanctum of All 2020-06-27 14:53:01 +02:00
swordshine
015bb428e4 Merge branch 'master' into 'master'
fix:missing some translation

See merge request core-developers/forge!2919
2020-06-27 11:03:23 +00:00
CCTV-1
645b6d89d2 fix:missing some translation. 2020-06-27 18:36:57 +08:00
Michael Kamensky
e60986e04e Merge branch 'SRPref' into 'master'
Screen reader flag with Initial Mana Cost handling

See merge request core-developers/forge!2916
2020-06-27 05:22:18 +00:00
swordshine
e8eb7abf48 Merge branch 'JMP.26' into 'master'
JMP - 26 June

See merge request core-developers/forge!2917
2020-06-27 03:25:16 +00:00
swordshine
425472cd54 Merge branch 'card_fix' into 'master'
fix "Sublime Epiphany"

See merge request core-developers/forge!2918
2020-06-27 03:24:37 +00:00
CCTV-1
48f109b7f6 fix:"Sublime Epiphany" can't select draw card player. 2020-06-27 09:56:55 +08:00
friarsol
df09cba493 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2020-06-26 21:40:38 -04:00
Northmoc
420db21d83 immolating_gyre.txt 2020-06-26 15:27:52 -04:00
Zachary Kline
54436ac2e3 Initial screen reader optimization. 2020-06-26 11:54:36 -07:00
Michael Kamensky
bb0d6c4606 Merge branch 'patch' into 'master'
Add Eon Frolicker

Closes #1362

See merge request core-developers/forge!2914
2020-06-26 17:11:57 +00:00
Michael Kamensky
dc058fccc1 Merge branch 'typo' into 'master'
pestilent_haze.txt fix

See merge request core-developers/forge!2915
2020-06-26 17:11:29 +00:00
Northmoc
8af5c1302d pestilent_haze.txt fix 2020-06-26 12:11:53 -04:00
swordshine
29a7c98185 Add Eon Frolicker 2020-06-26 22:18:54 +08:00
swordshine
bb4f4272ca Merge branch 'patch' into 'master'
More cards

Closes #1473

See merge request core-developers/forge!2912
2020-06-26 12:57:39 +00:00
swordshine
01183c454b Update ReplacementMill 2020-06-26 16:46:14 +08:00
swordshine
e3863b0712 Merge branch 'patch-7' into 'master'
Update es-ES.properties

See merge request core-developers/forge!2913
2020-06-26 06:50:52 +00:00
Churrufli
0cff9ebdf0 Update es-ES.properties 2020-06-26 06:43:24 +00:00
swordshine
5fc1f92309 Update Charmed Pendant to be compatible with ChangeText effects 2020-06-26 14:39:51 +08:00
swordshine
7b0247aba5 Merge branch 'thriving_land' into 'master'
JMP: add Thriving lands

See merge request core-developers/forge!2911
2020-06-26 05:50:33 +00:00
swordshine
f9f2b65e45 Add Sanctuary Blade 2020-06-26 13:42:14 +08:00
Hans Mackowiak
e4fb9d8a9b JMP: add Thriving lands 2020-06-26 07:02:20 +02:00
swordshine
f8dab4a4b2 Merge branch 'patch' into 'master'
Update CostMill

See merge request core-developers/forge!2910
2020-06-26 04:36:53 +00:00
swordshine
088062a93e Update Ai PaymentDecision 2020-06-26 12:14:27 +08:00
swordshine
6049a681e8 Update CostMill 2020-06-26 12:04:15 +08:00
swordshine
c622291c4e Merge branch 'patch' into 'master'
Update some scripts

See merge request core-developers/forge!2909
2020-06-26 03:38:28 +00:00
swordshine
450c7c5b71 Update some scripts 2020-06-26 11:33:38 +08:00
swordshine
b5ebf2185f Merge branch 'mill' into 'master'
Mill oracle updates

Closes #1469

See merge request core-developers/forge!2907
2020-06-26 03:30:28 +00:00
Tim Mocny
fa240f6dc2 Mill oracle updates 2020-06-26 03:30:27 +00:00
swordshine
b23b4a0888 Merge branch 'JMP.24' into 'master'
JMP - 24 June

See merge request core-developers/forge!2906
2020-06-26 03:24:46 +00:00
Tim Mocny
2fc2d623a8 JMP - 24 June 2020-06-26 03:24:44 +00:00
swordshine
58c7d69d27 Merge branch 'patch' into 'master'
More JMP cards (will update the CostMill after the oracle changes are finished)

See merge request core-developers/forge!2905
2020-06-26 03:22:13 +00:00
swordshine
730a7adf2a Update the scripts 2020-06-25 13:55:17 +08:00
swordshine
39d24724c6 Move cards to the JMP folder 2020-06-24 22:40:37 +08:00
swordshine
1053b3fdea JMP: Add Bruvac the Grandiloquent 2020-06-24 22:09:00 +08:00
swordshine
cbc3f6a568 JMP: Add Emiel the Blessed 2020-06-24 22:09:00 +08:00
swordshine
d007e86779 JMP: Add Zurzoth, Chaos Rider 2020-06-24 22:09:00 +08:00
Michael Kamensky
0503177c77 Merge branch 'master' into 'master'
Fix padding in quest draft tournaments screen on mobile.

See merge request core-developers/forge!2904
2020-06-24 13:21:23 +00:00
Michael Kamensky
ffc2187fea Merge branch 'patch' into 'master'
M21: Add Chandra's Incinerator

See merge request core-developers/forge!2903
2020-06-24 13:17:48 +00:00
Agetian
69d8aeb875 - Fix padding in quest draft tournaments screen on mobile. 2020-06-24 16:13:21 +03:00
swordshine
2e15415666 M21: Add Chandra's Incinerator 2020-06-24 16:07:13 +08:00
swordshine
17333c2ae9 Merge branch 'patch' into 'master'
More M21 cards

See merge request core-developers/forge!2902
2020-06-24 08:05:32 +00:00
swordshine
b9064dad3f Add Nine Lives 2020-06-24 15:16:41 +08:00
swordshine
b1c87c7f83 Two more M21 cards 2020-06-24 14:59:01 +08:00
swordshine
e8dba06444 More M21 cards 2020-06-24 14:52:29 +08:00
Michael Kamensky
46283cebd3 Merge branch 'M21.23' into 'master'
M21 - 23 June

See merge request core-developers/forge!2901
2020-06-24 04:31:53 +00:00
Tim Mocny
86573dedf1 M21 - 23 June 2020-06-24 04:31:52 +00:00
Michael Kamensky
f57ece280f Merge branch 'patch' into 'master'
Add Teferi, Timeless Voyager

Closes #1466

See merge request core-developers/forge!2900
2020-06-23 14:44:24 +00:00
swordshine
f4f0950f72 Merge branch 'M21.22' into 'master'
M21 - 22 June

See merge request core-developers/forge!2899
2020-06-23 13:55:45 +00:00
Tim Mocny
d507de72ff M21 - 22 June 2020-06-23 13:55:45 +00:00
swordshine
9b1c8e0980 Update the "UntilTheEndOfYourNextTurn" for Teferi, Timeless Voyager 2020-06-23 20:57:54 +08:00
swordshine
1e46df363f Merge branch 'M21.21a' into 'master'
M21 - 21 June (more)

See merge request core-developers/forge!2897
2020-06-23 01:56:53 +00:00
Tim Mocny
eface99d95 M21 - 21 June (more) 2020-06-23 01:56:52 +00:00
Michael Kamensky
9dc42e68fb Merge branch 'master' into 'master'
Fix a crash when accessing tournaments in a new quest

See merge request core-developers/forge!2898
2020-06-22 09:13:27 +00:00
Agetian
1a6b51bae2 - Fix a crash when accessing tournaments in a new quest 2020-06-22 12:02:29 +03:00
swordshine
39c6de95fd Merge branch 'M21.20' into 'master'
M21 - 20 June

See merge request core-developers/forge!2891
2020-06-22 01:42:33 +00:00
Tim Mocny
d3275d78fb M21 - 20 June 2020-06-22 01:42:33 +00:00
swordshine
29afbd67a9 Merge branch 'typo' into 'master'
dream_leash.txt text update

See merge request core-developers/forge!2896
2020-06-22 01:05:02 +00:00
Northmoc
d4aefdeca1 dream_leash.txt text update 2020-06-21 14:27:06 -04:00
Michael Kamensky
0df56097af Merge branch 'M21.21' into 'master'
M21 - 21 June

See merge request core-developers/forge!2895
2020-06-21 14:09:22 +00:00
Tim Mocny
3bbadca04f M21 - 21 June 2020-06-21 14:09:21 +00:00
Michael Kamensky
9034b28747 Merge branch 'new_cards' into 'master'
some m21 cards

See merge request core-developers/forge!2893
2020-06-21 14:04:21 +00:00
Michael Kamensky
332116509d Merge branch 'patch' into 'master'
Jumpstart: Add Tinybones, Trinket Thief

See merge request core-developers/forge!2894
2020-06-21 11:23:56 +00:00
CCTV-1
37c846330f response code review 2020-06-21 12:20:14 +08:00
swordshine
93dfb28b70 Jumpstart: Add Tinybones, Trinket Thief 2020-06-21 11:51:33 +08:00
Michael Kamensky
20e41c4fda Merge branch 'M21support' into 'master'
M21 support

See merge request core-developers/forge!2892
2020-06-21 03:30:09 +00:00
Tim Mocny
5d61cd795a M21 support 2020-06-21 03:30:09 +00:00
CCTV-1
4513976f20 some m21 cards 2020-06-21 09:17:41 +08:00
Michael Kamensky
ba0b5f813b Merge branch 'patch' into 'master'
Add Basri Ket

See merge request core-developers/forge!2890
2020-06-20 18:33:46 +00:00
swordshine
1e3531efc6 Add AI hints 2020-06-20 23:03:24 +08:00
swordshine
0e9287ba2f Merge branch 'M21.19' into 'master'
M21 - 19 June

See merge request core-developers/forge!2887
2020-06-20 14:35:25 +00:00
Tim Mocny
f65ec5455f M21 - 19 June 2020-06-20 14:35:24 +00:00
swordshine
26e0bdb545 Merge branch 'typo' into 'master'
biomass_mutation.txt typo

See merge request core-developers/forge!2888
2020-06-20 14:33:16 +00:00
swordshine
dc300a9929 Update Basri Ket's second ability (the script is implemented by Tim Mocny) 2020-06-20 22:30:23 +08:00
swordshine
d171577127 Merge branch 'patch' into 'master'
Add Carrion Grub

See merge request core-developers/forge!2889
2020-06-20 14:26:28 +00:00
swordshine
c9e0d7722c Add Carrion Grub 2020-06-20 21:52:09 +08:00
Northmoc
a95321de4e biomass_mutation.txt typo 2020-06-20 08:21:02 -04:00
Michael Kamensky
2ec4479994 Merge branch 'new_cards' into 'master'
some m21 cards

See merge request core-developers/forge!2886
2020-06-19 14:32:34 +00:00
CCTV-1
414b8b0bae response codereview 2020-06-19 19:16:04 +08:00
CCTV-1
b4686e5643 some m21 cards 2020-06-19 11:13:42 +08:00
Michael Kamensky
28f6edaccb Merge branch 'new_cards' into 'master'
some m21 cards

See merge request core-developers/forge!2884
2020-06-18 12:39:03 +00:00
CCTV-1
2359a4b435 fix typo 2020-06-18 16:03:43 +08:00
CCTV-1
5b0bdff50c response codereview 2020-06-18 11:32:47 +08:00
Michael Kamensky
a7c5dd2681 Merge branch 'patch-6' into 'master'
Update de-DE.properties

See merge request core-developers/forge!2876
2020-06-18 03:14:14 +00:00
Michael Kamensky
4d9bbef480 Merge branch 'M21.16' into 'master'
M21 - 16 June

See merge request core-developers/forge!2883
2020-06-18 03:13:48 +00:00
Tim Mocny
20b599d52a M21 - 16 June 2020-06-18 03:13:48 +00:00
Michael Kamensky
e9bcdcc133 Merge branch 'fix_menace_counters' into 'master'
Fix menace counters

See merge request core-developers/forge!2885
2020-06-18 03:12:01 +00:00
Sol
90dc1a6898 Update frillscare_mentor.txt 2020-06-18 02:40:47 +00:00
Sol
3a56cd60b8 Update blood_curdle.txt 2020-06-18 02:40:20 +00:00
CCTV-1
51ec7b049b light_of_promise.txt 2020-06-18 08:59:37 +08:00
CCTV-1
1061b2e605 some m21 cards 2020-06-17 21:09:20 +08:00
swordshine
a71f8e2932 Merge branch 'M21.15' into 'master'
M21 - 15 June

See merge request core-developers/forge!2882
2020-06-17 01:33:24 +00:00
Tim Mocny
90e9f738b0 M21 - 15 June 2020-06-17 01:33:23 +00:00
swordshine
68a33b1322 Merge branch 'typos' into 'master'
Typos :: TgtPromp$ -> TgtPrompt$

See merge request core-developers/forge!2881
2020-06-16 04:50:01 +00:00
Northmoc
d74f4aa671 TgtPromp$ -> TgtPrompt$ 2020-06-15 16:10:24 -04:00
Michael Kamensky
498acbd496 Merge branch 'new_cards' into 'master'
some m21 cards

See merge request core-developers/forge!2880
2020-06-15 12:49:09 +00:00
CCTV-1
56cb550850 response codereview 2020-06-15 18:24:00 +08:00
CCTV-1
f28b9c8d03 code cleanup 2020-06-15 16:15:07 +08:00
CCTV-1
9a00c7d02a response codereview 2020-06-15 15:59:49 +08:00
CCTV-1
a7c69d6841 some m21 cards 2020-06-15 10:42:32 +08:00
Michael Kamensky
63f9457162 Merge branch '1451-archenemy-modes-are-not-working-as-intended' into 'master'
Resolve "Archenemy modes are not working as intended"

Closes #1451

See merge request core-developers/forge!2878
2020-06-14 03:19:57 +00:00
Hans Mackowiak
0a18cff766 Player: fix Scheme not triggering for set in motion 2020-06-13 21:15:39 +02:00
Andreas Bendel
95e5316336 Update de-DE.properties 2020-06-13 15:37:23 +00:00
swordshine
9888263507 Merge branch 'M21ed.rough' into 'master'
M21 - edition file

See merge request core-developers/forge!2862
2020-06-13 13:50:14 +00:00
Tim Mocny
2001d6e685 M21 - edition file 2020-06-13 13:50:14 +00:00
swordshine
39899fe859 Merge branch 'new_cards' into 'master'
some m21 cards

See merge request core-developers/forge!2875
2020-06-13 13:49:41 +00:00
CCTV-1
1ef8d32dfe response codereview 2020-06-13 21:23:42 +08:00
CCTV-1
9b16919c37 four m21 cards 2020-06-13 21:09:52 +08:00
CCTV-1
a4f70cea78 some m21 cards 2020-06-13 20:34:08 +08:00
swordshine
103669eee3 Merge branch 'hounddog' into 'master'
Hound -> Dog

Closes #1454

See merge request core-developers/forge!2868
2020-06-13 12:17:05 +00:00
Tim Mocny
25260f8d7c Hound -> Dog 2020-06-13 12:17:05 +00:00
swordshine
eeec573798 Merge branch 'M21.11' into 'master'
M21 - 11 June

See merge request core-developers/forge!2872
2020-06-13 05:09:45 +00:00
Tim Mocny
f6e56ee23f M21 - 11 June 2020-06-13 05:09:44 +00:00
Michael Kamensky
d175ca87c7 Merge branch 'new_cards' into 'master'
some m21 cards

See merge request core-developers/forge!2873
2020-06-12 20:25:52 +00:00
CCTV-1
a59412550d some m21 cards 2020-06-12 13:18:15 +08:00
CCTV-1
dd78ab67eb fix card 'Havoc Jester' P/T 2020-06-12 13:17:31 +08:00
swordshine
05100f5689 Merge branch 'M21.10' into 'master'
M21 - 10 June

See merge request core-developers/forge!2869
2020-06-12 01:44:02 +00:00
Tim Mocny
69b30e4313 M21 - 10 June 2020-06-12 01:44:02 +00:00
swordshine
d509c87371 Merge branch 'new_cards' into 'master'
some m21 cards

See merge request core-developers/forge!2871
2020-06-12 01:42:41 +00:00
CCTV-1
aa15b7d5da remove LegacyImage 2020-06-12 07:53:30 +08:00
CCTV-1
674514c158 some m21 cards 2020-06-11 20:50:51 +08:00
Michael Kamensky
961fdae04a Merge branch 'cube_patch' into 'master'
update cubes

See merge request core-developers/forge!2870
2020-06-11 04:51:46 +00:00
swordshine
73ac827d0b Merge branch 'lord.AI' into 'master'
lord_of_atlantis.txt DeckHints

See merge request core-developers/forge!2867
2020-06-11 01:17:27 +00:00
swordshine
6961d63127 Merge branch 'tahngarth.typo' into 'master'
tahngarth_first_mate.txt typo

See merge request core-developers/forge!2866
2020-06-11 01:17:10 +00:00
CCTV-1
19f36789c2 support amaz peasant cube 2020-06-11 08:28:03 +08:00
CCTV-1
31dea9a94d update mtga cube list 2020-06-11 08:27:37 +08:00
CCTV-1
c6839717f9 remove duplicate cube 2020-06-11 08:27:14 +08:00
Northmoc
5a0f5bc4ba lord_of_atlantis.txt DeckHints 2020-06-10 10:27:35 -04:00
Northmoc
ead2f62b5f tahngarth_first_mate.txt CARDNAME missing 2020-06-10 09:18:46 -04:00
Michael Kamensky
779db06765 Merge branch 'M21.9' into 'master'
M21 - 9 June

See merge request core-developers/forge!2865
2020-06-10 05:29:04 +00:00
Tim Mocny
288fe96a94 M21 - 9 June 2020-06-10 05:29:02 +00:00
Michael Kamensky
f3b86e0532 Merge branch 'tamitypo' into 'master'
tamiyo_field_researcher.txt typo

See merge request core-developers/forge!2864
2020-06-10 05:28:03 +00:00
Michael Kamensky
deeccf93d4 Merge branch 'chandraflamecat' into 'master'
chandra_flames_catalyst.txt

See merge request core-developers/forge!2863
2020-06-10 05:27:25 +00:00
Northmoc
c03848dc1d tamiyo_field_researcher.txt typo 2020-06-09 10:49:48 -04:00
Northmoc
a44b2a31cd chandra_flames_catalyst.txt 2020-06-09 10:48:56 -04:00
Michael Kamensky
9370dc0036 Merge branch 'patch-6' into 'master'
Update de-DE.properties

See merge request core-developers/forge!2857
2020-06-09 08:17:46 +00:00
Michael Kamensky
4ef89b9963 Merge branch 'M21.8' into 'master'
M21 - 8 June

See merge request core-developers/forge!2861
2020-06-09 05:18:55 +00:00
Tim Mocny
30dcdfb88a M21 - 8 June 2020-06-09 05:18:55 +00:00
Michael Kamensky
833395fb92 Merge branch 'dnaga' into 'master'
desiccated_naga.txt AI update

See merge request core-developers/forge!2859
2020-06-08 14:48:06 +00:00
Michael Kamensky
52b5271af5 Merge branch 'M21.6' into 'master'
liliana_death_mage.txt

See merge request core-developers/forge!2860
2020-06-08 14:47:57 +00:00
Northmoc
b8a7696f40 liliana_death_mage.txt 2020-06-08 09:34:18 -04:00
Northmoc
3cb80c43bf desiccated_naga.txt AI update 2020-06-08 09:25:27 -04:00
Sol
225247d9dc Merge branch 'trackerRevealLKI' into 'master'
PlayerControllerHuman: fix reveal on LKI card list

See merge request core-developers/forge!2848
2020-06-07 14:44:16 +00:00
Hans Mackowiak
2db862ce2d PlayerControllerHuman: fix reveal on LKI card list 2020-06-07 14:44:16 +00:00
Andreas Bendel
e0a1990014 Update de-DE.properties
Typo in translation of lblSelectUpToNumCardFromPlayerZone
2020-06-07 09:33:10 +00:00
Michael Kamensky
68a559552d Merge branch 'M21.6' into 'master'
M21 - 6 June

See merge request core-developers/forge!2856
2020-06-06 15:26:21 +00:00
Michael Kamensky
2ddcb34a08 Merge branch 'chandra' into 'master'
Chandra SSB

See merge request core-developers/forge!2855
2020-06-06 15:24:33 +00:00
Michael Kamensky
28f794743c Merge branch 'M21.5' into 'master'
M21 - 5 June

See merge request core-developers/forge!2853
2020-06-06 15:24:30 +00:00
Tim Mocny
5b830aeb37 M21 - 5 June 2020-06-06 15:24:28 +00:00
Northmoc
499f33ed69 teferis_protege.txt 2020-06-06 11:02:40 -04:00
Northmoc
46ead353d0 ss3 2020-06-06 10:37:03 -04:00
Michael Kamensky
36c04efbff Merge branch 'trampleFix' into 'master'
PlayerControllerHuman: fix trample assign to defender, fix trample counter

See merge request core-developers/forge!2854
2020-06-06 09:50:49 +00:00
Hans Mackowiak
653d214027 PlayerControllerHuman: fix trample assign to defender, fix trample counter 2020-06-06 09:10:05 +02:00
Michael Kamensky
f60fdf3fa5 Merge branch 'M21.4' into 'master'
M21 - 4 June

See merge request core-developers/forge!2851
2020-06-06 03:35:43 +00:00
Tim Mocny
b3015128a1 M21 - 4 June 2020-06-06 03:35:43 +00:00
Sol
2c038396b6 Update retribution_of_the_ancients.txt 2020-06-05 16:17:28 +00:00
Michael Kamensky
2370c2c8ba Merge branch 'counterTypeNetwork' into 'master'
CounterType: needs to be serializable for Network

See merge request core-developers/forge!2852
2020-06-05 05:56:19 +00:00
Hans Mackowiak
d743bd5168 CounterType: needs to be serializable for Network 2020-06-05 07:42:15 +02:00
Michael Kamensky
4880448102 Merge branch 'migrate.upcoming' into 'master'
Migrate remaining IKO/C20 from upcoming

See merge request core-developers/forge!2850
2020-06-04 17:49:38 +00:00
Northmoc
b34fd44cfd Migrate remaining IKO/C20 from upcoming 2020-06-04 12:08:34 -04:00
Michael Kamensky
4acfd71123 Merge branch 'references' into 'master'
References cleanups

Closes #1395

See merge request core-developers/forge!2813
2020-06-02 02:50:34 +00:00
Tim Mocny
dd72be2f7a References cleanups 2020-06-02 02:50:34 +00:00
Michael Kamensky
b140ca57be Merge branch 'patch-7' into 'master'
Update Secret Lair Drop Series.txt

See merge request core-developers/forge!2845
2020-06-02 02:49:53 +00:00
Michael Kamensky
14f2b25c45 Merge branch 'patch-8' into 'master'
Update Amalia Sheran Sharm 1.dck

Closes #1440

See merge request core-developers/forge!2846
2020-06-02 02:49:47 +00:00
Michael Kamensky
8bfcd1815a Merge branch 'sedgeSliverFix' into 'master'
Card: fix addChangedCardTraits when multiple StaticAbilities has the same timestamp

See merge request core-developers/forge!2849
2020-06-02 02:49:04 +00:00
Sol
95a8f5c031 Update pyrewild_shaman.txt 2020-06-02 02:34:22 +00:00
Hans Mackowiak
1b40987bbd Card: fix addChangedCardTraits when multiple StaticAbilities has the same timestamp 2020-06-01 22:04:55 +02:00
Sol
febeaf4eec Merge branch '1Junbnr' into 'master'
1 June bannings

See merge request core-developers/forge!2847
2020-06-01 19:40:07 +00:00
Tim Mocny
1079ad8e83 1 June bannings 2020-06-01 19:40:07 +00:00
Tim Mocny
d67a1e7534 Update Amalia Sheran Sharm 1.dck 2020-06-01 14:20:10 +00:00
Tim Mocny
46b3c622f0 Update Secret Lair Drop Series.txt 2020-06-01 14:15:43 +00:00
Michael Kamensky
8714399b51 Merge branch '1441-avoid-bad-performance-pattern-involving-interger-parseint' into 'master'
Resolve "Avoid bad performance pattern involving Interger.parseInt"

Closes #1441

See merge request core-developers/forge!2844
2020-05-31 14:22:26 +00:00
Hans Mackowiak
19c07f5bef Resolve "Avoid bad performance pattern involving Interger.parseInt" 2020-05-31 14:22:25 +00:00
Michael Kamensky
33e7cde2d6 Merge branch 'sld' into 'master'
SLD summer drops

Closes #1438

See merge request core-developers/forge!2842
2020-05-31 05:25:55 +00:00
Michael Kamensky
12d6790495 Merge branch 'cycling' into 'master'
flourishing_fox.txt fix

Closes #1435

See merge request core-developers/forge!2843
2020-05-31 05:25:47 +00:00
Northmoc
37f2e75dc4 flourishing_fox.txt fix 2020-05-30 20:17:13 -04:00
Northmoc
019969d804 SLD summer drops 2020-05-30 20:08:15 -04:00
Michael Kamensky
8564d30efc Merge branch 'wrappedAbilityCleanup' into 'master'
WrappedAbility & Trigger: some cleanup

Closes #1400

See merge request core-developers/forge!2841
2020-05-30 17:19:31 +00:00
Hans Mackowiak
51c15a5a4c WrappedAbility & Trigger: some cleanup 2020-05-30 08:29:53 +02:00
Michael Kamensky
3a475f44e4 Merge branch 'cultivate' into 'master'
Cultivate and etc round 2

See merge request core-developers/forge!2836
2020-05-30 03:50:58 +00:00
Tim Mocny
47f2123286 Cultivate and etc round 2 2020-05-30 03:50:57 +00:00
Hans Mackowiak
65ef844444 Merge branch '1439-cardtype-kills-performance' into 'master'
Resolve "CardType kills performance"

Closes #1439

See merge request core-developers/forge!2840
2020-05-29 08:32:19 +00:00
Hans Mackowiak
57b4135f95 CardType: use stringTo Maps as Cache again 2020-05-29 09:44:59 +02:00
Michael Kamensky
3d32915d9f Merge branch 'guiChoiceFixGameCache' into 'master'
Player Human: fix chooseSingleEntityForEffect

See merge request core-developers/forge!2839
2020-05-29 07:19:44 +00:00
Hans Mackowiak
379f9699e8 Player Human: fix chooseSingleEntityForEffect 2020-05-29 09:12:08 +02:00
Hans Mackowiak
0e831f88b9 Merge branch '1434-iko-shredded-sails-incorrect-dmg-to-flying-creature' into 'master'
Resolve "[IKO] Shredded Sails, incorrect dmg to flying creature"

Closes #1434

See merge request core-developers/forge!2838
2020-05-28 08:45:59 +00:00
Hans Mackowiak
8e0c1f4fe3 Update shredded_sails.txt 2020-05-28 07:15:42 +00:00
Hans Mackowiak
d7293c7830 Update boot_nipper.txt 2020-05-27 19:29:36 +00:00
Sol
817b8bf15b Merge branch 'gameCacheGone' into 'master'
PlayerControllerHuman: remove gameCache entirely, always ask Game for Card by ID

See merge request core-developers/forge!2837
2020-05-27 02:18:30 +00:00
Sol
26694ffa5c Merge branch 'counterStringType' into 'master'
CounterType: use String inside CounterType to handle KeywordCounter

See merge request core-developers/forge!2798
2020-05-27 00:17:28 +00:00
Hans Mackowiak
6fde7149a4 - Basic support for CountersMoveAi (e.g. The Ozolith) so at least it doesn't crash, needs improvement. 2020-05-27 00:17:28 +00:00
Hans Mackowiak
815b2bc4aa PlayerControllerHuman: remove gameCache entirely, always ask Game for Card by ID 2020-05-26 23:21:44 +02:00
swordshine
5c7a0adf33 Merge branch 'cultivate' into 'master'
Cultivate and such UI update

See merge request core-developers/forge!2835
2020-05-26 01:34:11 +00:00
Tim Mocny
3a2c66df0b Cultivate and such UI update 2020-05-26 01:34:11 +00:00
Michael Kamensky
b249560dbb Merge branch 'replacementEffectString' into 'master'
ReplacementEffect: add HostCard Name to toString for Human GUI

See merge request core-developers/forge!2834
2020-05-25 16:40:27 +00:00
Hans Mackowiak
a165f314d3 Update Keyword: fix Bloodthirst 2020-05-25 13:59:37 +00:00
Hans Mackowiak
0c27a41566 ReplacementEffect: add HostCard Name to toString for Human GUI 2020-05-25 14:09:54 +02:00
Hans Mackowiak
955a16f1e0 Update ongoing_investigation.txt 2020-05-24 16:09:04 +00:00
Michael Kamensky
1ced1058c9 Merge branch 'gameCacheRework' into 'master'
Human Play: try to remove usage of game.getCard

See merge request core-developers/forge!2804
2020-05-24 11:02:52 +00:00
Hans Mackowiak
ea26f6c689 Human Play: try to remove usage of game.getCard 2020-05-24 11:02:51 +00:00
Michael Kamensky
fc706c8035 Merge branch '1431-npe-in-computerutil-canregenerate' into 'master'
Resolve "NPE in ComputerUtil.canRegenerate"

Closes #1431

See merge request core-developers/forge!2833
2020-05-23 14:00:18 +00:00
Hans Mackowiak
74589d59e4 TokenAi: fix setting Owner of the Token to prevent NPE 2020-05-23 15:42:56 +02:00
Sol
70046c5261 Merge branch 'patch-7' into 'master'
Update liliana_dreadhorde_general.txt

See merge request core-developers/forge!2832
2020-05-22 14:50:16 +00:00
Tim Mocny
3165c57b0e Update liliana_dreadhorde_general.txt 2020-05-22 12:48:43 +00:00
Hans Mackowiak
27968cd561 Merge branch 'buildPluginFix' into 'master'
build-helper-maven-plugin: update version to 3.1.0

See merge request core-developers/forge!2827
2020-05-22 06:44:31 +00:00
swordshine
9cb4911e29 Merge branch 'friarsol-master-patch-87682' into 'master'
Fix Mystic Barrier swallowing triggers

See merge request core-developers/forge!2831
2020-05-22 05:22:11 +00:00
Sol
933fe7ad49 Fix Mystic Barrier swallowing triggers 2020-05-22 05:22:11 +00:00
friarsol
b13503c8a7 Merge remote-tracking branch 'origin/master' 2020-05-21 21:47:52 -04:00
Sol
2bdd598565 Merge branch 'patch-7' into 'master'
typo precognition_field.txt

See merge request core-developers/forge!2830
2020-05-22 00:48:42 +00:00
friarsol
81bcacefc0 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2020-05-21 20:47:24 -04:00
Tim Mocny
215cd20b90 typo precognition_field.txt 2020-05-21 23:50:45 +00:00
Michael Kamensky
0701fd6bbc Merge branch '1424-oome-by-zone-lateststatecardsaddedthisturn' into 'master'
Resolve "OOME by Zone.latestStateCardsAddedThisTurn"

Closes #1424

See merge request core-developers/forge!2828
2020-05-21 15:58:24 +00:00
Michael Kamensky
3e89f64944 Merge branch 'master' into 'master'
Fix DestroyAi hanging when considering auras that control AI's cards

Closes #1425

See merge request core-developers/forge!2829
2020-05-21 14:31:33 +00:00
Agetian
3041788be1 - Fix DestroyAi hanging when considering auras that control AI's cards 2020-05-21 17:18:48 +03:00
Hans Mackowiak
f5e5ddbbb9 MagicStack: call resetCardsAddedThisTurn on onNextTurn 2020-05-21 10:36:03 +00:00
Michael Kamensky
c0578fb246 Merge branch 'synchronize-activator-rd2' into 'master'
Synchronize activator Round 2

See merge request core-developers/forge!2825
2020-05-21 05:14:13 +00:00
Hans Mackowiak
0489a2ba83 Update tireless_tracker fix TrigInvestigate 2020-05-21 00:45:09 +00:00
Michael Kamensky
25109de700 Merge branch 'digMultipleChooseZone' into 'master'
DigMultipleEffect: add extra ChosenZone to select N from the Chosen

See merge request core-developers/forge!2826
2020-05-20 16:35:23 +00:00
Hans Mackowiak
d86aa31ab8 DigMultipleEffect: add extra ChosenZone to select N from the Chosen 2020-05-20 16:35:23 +00:00
Hans Mackowiak
0846017766 build-helper-maven-plugin: update version to 3.1.0 to fix version can neither be null, empty nor blank 2020-05-20 11:43:15 +02:00
rory
a964f5b71b checking for unique mana symbols correctly considers generic mana costs on split cards 2020-05-19 21:58:13 -04:00
rory
d311231c96 move CantPayGenericCosts higher up in mana restrictions so that the activated restriction doesn't short circuit for loop 2020-05-19 21:58:13 -04:00
rory
b2018aafe6 Jegantha, the Wellspring 2020-05-19 21:58:13 -04:00
Northmoc
7ab4d13507 Vintage.txt Lurrus fix 2020-05-19 21:58:13 -04:00
friarsol
7a6409c11c Synchronize activator Round 2 2020-05-19 21:54:47 -04:00
friarsol
25fb9857d8 Synchronize activator Round 2 2020-05-19 21:53:33 -04:00
swordshine
b7b2c88916 Merge branch 'jegantha' into 'master'
Jegantha

See merge request core-developers/forge!2802
2020-05-20 00:49:58 +00:00
swordshine
2b714672a2 Merge branch 'vintagebnr' into 'master'
Vintage.txt Lurrus fix

See merge request core-developers/forge!2824
2020-05-20 00:49:24 +00:00
Northmoc
0ed4670cfb Vintage.txt Lurrus fix 2020-05-19 09:03:25 -04:00
Sol
fa610b4bfd Merge branch 'revert-c793065c' into 'master'
Revert "Merge branch 'synchronize-activator-restriction' into 'master'"

See merge request core-developers/forge!2823
2020-05-19 12:01:48 +00:00
Sol
2af1895e26 Revert "Merge branch 'synchronize-activator-restriction' into 'master'"
This reverts merge request !2821
2020-05-19 11:45:47 +00:00
Michael Kamensky
b919ccb50c Merge branch 'master' into 'master'
Fix a typo in code.

See merge request core-developers/forge!2822
2020-05-19 07:50:26 +00:00
Agetian
87ed2140b4 - Fix a typo in code. 2020-05-19 10:38:06 +03:00
Michael Kamensky
b9d5326c30 Merge branch 'tokenCopyInvestigateRework' into 'master'
Effects: Effects that create Tokens now use common base class

Closes #1379 and #1350

See merge request core-developers/forge!2816
2020-05-19 03:46:43 +00:00
Hans Mackowiak
0af1085f79 Effects: Effects that create Tokens now use common base class 2020-05-19 03:46:43 +00:00
swordshine
c793065cae Merge branch 'synchronize-activator-restriction' into 'master'
Synchronize activator restriction

See merge request core-developers/forge!2821
2020-05-19 01:11:22 +00:00
swordshine
248a2447d2 Merge branch 'may2020bnr' into 'master'
18 May 2020 bans

See merge request core-developers/forge!2820
2020-05-19 01:08:37 +00:00
Tim Mocny
df2932bec5 18 May 2020 bans 2020-05-19 01:08:37 +00:00
friarsol
b7fb854389 Fix Personal Incarnation activator missing owner 2020-05-18 20:42:03 -04:00
Michael Kamensky
7c322e1e33 Merge branch 'quest-menu-update' into 'master'
Quest menu update

See merge request core-developers/forge!2817
2020-05-18 03:29:30 +00:00
Sol
74b3ecd8ce Quest menu update 2020-05-18 03:29:30 +00:00
Michael Kamensky
82060740c6 Merge branch 'fix-get-best-land' into 'master'
Fix FindBestLand ai

Closes #1415

See merge request core-developers/forge!2818
2020-05-18 03:24:22 +00:00
friarsol
45b1b3f38b Convert all Activator Restrictions to a combined key/value 2020-05-17 23:16:52 -04:00
Michael Kamensky
f72526fa08 Merge branch 'fix-capricopian' into 'master'
Fix Capricopian

Closes #1411

See merge request core-developers/forge!2819
2020-05-18 03:13:35 +00:00
friarsol
e96fa0cc96 Fix Capricopian 2020-05-17 21:40:09 -04:00
friarsol
7966684424 Fix FindBestLand ai 2020-05-17 21:12:03 -04:00
Michael Kamensky
56b5a6d3c2 Merge branch 'master' into 'master'
IKO and C20 initial AI hints.

See merge request core-developers/forge!2815
2020-05-17 17:59:06 +00:00
Agetian
0516693eec - IKO and C20 initial AI hints. 2020-05-17 20:49:03 +03:00
Michael Kamensky
8c40ba9ef3 Merge branch '1407-lazav-dimir-mastermind-causes-npe' into 'master'
Resolve ""Lazav, Dimir Mastermind" causes NPE"

Closes #1407

See merge request core-developers/forge!2814
2020-05-16 16:53:31 +00:00
Hans Mackowiak
b12f97daa2 TriggerChangesZone: Trigger on leaving the battlefield look back in time using LKI 2020-05-15 14:15:34 +02:00
rory
815a0f5da9 checking for unique mana symbols correctly considers generic mana costs on split cards 2020-05-14 11:51:40 -07:00
Michael Kamensky
2e319a4c9b Merge branch 'discardLengFix' into 'master'
Fix Discard cost with Library of Leng

See merge request core-developers/forge!2812
2020-05-14 08:26:18 +00:00
Hans Mackowiak
7b151bf78a Fix Discard cost with Library of Leng 2020-05-14 08:26:18 +00:00
Michael Kamensky
f091c0592e Merge branch 'master' into 'master'
Fix AI cheating with ChooseTypeEffect

Closes #1410

See merge request core-developers/forge!2811
2020-05-14 05:31:04 +00:00
Agetian
76ade204bb - Fix AI cheating with ChooseTypeEffect 2020-05-14 08:24:03 +03:00
swordshine
046f2a58d9 Merge branch 'patch-6' into 'master'
Update de-DE.properties

See merge request core-developers/forge!2810
2020-05-14 00:51:04 +00:00
Andreas Bendel
a0e2de0aec Update de-DE.properties
more than 100 new lines tranlated
2020-05-13 22:10:41 +00:00
Michael Kamensky
fffb01a81c Merge branch 'companion-ai' into 'master'
Add "AI" for Companion choosing

Closes #1406

See merge request core-developers/forge!2809
2020-05-13 05:16:08 +00:00
friarsol
022875f07a Add "AI" for Companion choosing 2020-05-12 21:26:41 -04:00
Michael Kamensky
e60daf0536 Merge branch 'brawl' into 'master'
Add IKO to Brawl

See merge request core-developers/forge!2808
2020-05-12 16:58:48 +00:00
Tim Mocny
b17c4a188c Add IKO to Brawl 2020-05-12 16:58:48 +00:00
Michael Kamensky
da32b24fa8 Merge branch 'costPaymentMatrixFix' into 'master'
CostPayment: reset CardMatrix after being used

Closes #1408

See merge request core-developers/forge!2807
2020-05-12 10:19:29 +00:00
Hans Mackowiak
d25825c595 CostPayment: reset CardMatrix after being used 2020-05-12 10:19:29 +00:00
Michael Kamensky
15eca3f80a Merge branch 'tokenPermanentFix' into 'master'
Effects and Emblems are not token, cards on the field without card types are sill permanents

See merge request core-developers/forge!2806
2020-05-12 06:57:53 +00:00
Hans Mackowiak
3f428ee7d4 Effects and Emblems are not token, cards on the field without card types are sill permanents 2020-05-12 06:57:53 +00:00
swordshine
df9b462353 Merge branch 'patch-6' into 'master'
Update de-DE.properties

See merge request core-developers/forge!2803
2020-05-12 06:28:10 +00:00
rory
77c15b1673 move CantPayGenericCosts higher up in mana restrictions so that the activated restriction doesn't short circuit for loop 2020-05-11 16:46:48 -07:00
Sol
c4a1f22584 Merge branch 'newBranch' into 'master'
Fix CardArtCache,LoadingCache Dispose Textures

See merge request core-developers/forge!2805
2020-05-11 15:23:08 +00:00
swordshine
369a366b4f Merge branch 'master' into 'master'
translate some text

See merge request core-developers/forge!2502
2020-05-11 10:57:49 +00:00
Anthony Calosa
bcb626872f Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-05-11 11:13:38 +08:00
Anthony Calosa
deb0d7e2f1 Fix CardArtCache,LoadingCache Dispose Textures 2020-05-11 10:22:13 +08:00
Sol
75ab38836e Update combine_guildmage.txt 2020-05-11 01:48:06 +00:00
Andreas Bendel
68cece62c3 Update de-DE.properties
Typo fixes
2020-05-10 21:34:10 +00:00
rory
51e945df72 Merge branch 'master' into jegantha 2020-05-10 11:39:14 -07:00
rory
4eb21594b4 Jegantha, the Wellspring 2020-05-10 11:34:07 -07:00
Michael Kamensky
6362390e6a Merge branch 'master' into 'master'
Fix NPE in chooseCardName (AI)

Closes #1404

See merge request core-developers/forge!2801
2020-05-10 16:30:20 +00:00
Agetian
e7f0b33dd2 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2020-05-10 19:03:28 +03:00
Agetian
b0300edc9f - Fix NPE in chooseCardName (AI) 2020-05-10 19:03:01 +03:00
Michael Kamensky
d8a5e573da Merge branch 'spellCopyPaidFix' into 'master'
SpellAbility: copy PaidHash when copy spell ability

See merge request core-developers/forge!2800
2020-05-10 15:32:08 +00:00
Hans Mackowiak
bb84b4a4d4 SpellAbility: copy PaidHash when copy spell ability 2020-05-10 16:53:34 +02:00
CCTV-1
b66eb6266d translate Companion choose dialog,update translations,update chinese characters. 2020-05-10 16:40:24 +08:00
CCTV-1
e0b61e7453 Merge remote-tracking branch 'upstream/master' 2020-05-10 16:36:25 +08:00
Michael Kamensky
7018beab62 Merge branch 'patch-6' into 'master'
Update de-DE.properties

See merge request core-developers/forge!2789
2020-05-10 05:28:35 +00:00
swordshine
09fba334d4 Merge branch 'fixes' into 'master'
Fixes

Closes #1396

See merge request core-developers/forge!2799
2020-05-10 03:11:28 +00:00
Tim Mocny
f6d6e45fed Fixes 2020-05-10 03:11:28 +00:00
Michael Kamensky
7d15b021aa Merge branch 'yannik' into 'master'
yannik_scavenging_sentinel.txt fixes

See merge request core-developers/forge!2796
2020-05-09 16:57:45 +00:00
Tim Mocny
83774d57df yannik_scavenging_sentinel.txt fixes 2020-05-09 16:57:45 +00:00
Michael Kamensky
414665166c Merge branch 'historic' into 'master'
Historic updates

See merge request core-developers/forge!2797
2020-05-09 04:07:32 +00:00
Tim Mocny
ab6d856880 Historic updates 2020-05-09 04:07:31 +00:00
Sol
538430009b Merge branch 'patch' into 'master'
Update Companion cards

See merge request core-developers/forge!2793
2020-05-09 01:06:50 +00:00
Sol
1db29e8846 Merge branch 'newBranch' into 'master'
Hotfix Android Crash on New User Installation

See merge request core-developers/forge!2794
2020-05-09 01:00:10 +00:00
Sol
6ad5e73089 Merge branch 'recentsets' into 'master'
Some recent set support

See merge request core-developers/forge!2795
2020-05-09 00:56:07 +00:00
Tim Mocny
e4c0a3e6b3 Some recent set support 2020-05-09 00:56:07 +00:00
Anthony Calosa
311c8138d6 Hotfix Android Crash on New User Installation 2020-05-09 06:36:45 +08:00
swordshine
447453977d Update scripts 2020-05-08 22:52:10 +08:00
swordshine
085c9f0042 Add Umori, the Collector 2020-05-08 21:58:28 +08:00
swordshine
ed4be69c93 Update Gyruda 2020-05-08 21:56:39 +08:00
swordshine
97bcf8f9c7 Update more Companion cards based on Northmoc's scripts 2020-05-08 19:49:37 +08:00
swordshine
2fd4f9c31f Update Companion restriction for Lurrus and Zirda 2020-05-08 15:08:18 +08:00
Michael Kamensky
d9ee072064 Merge branch 'totemDamageFix' into 'master'
DamageDealEffect: fix removeDamage

See merge request core-developers/forge!2792
2020-05-08 05:53:19 +00:00
Hans Mackowiak
d1199e2698 DamageDealEffect: fix removeDamage 2020-05-08 05:53:19 +00:00
swordshine
d5b1799cba Update some scripts 2020-05-08 13:08:34 +08:00
Michael Kamensky
a1d241dd6e Merge branch 'companion' into 'master'
Companion

See merge request core-developers/forge!2714
2020-05-08 03:43:23 +00:00
Michael Kamensky
82d11283f7 Merge branch 'lethalDamagePTFix' into 'master'
LethalDamage: update Property when PT or counter changes

See merge request core-developers/forge!2791
2020-05-08 03:38:44 +00:00
Hans Mackowiak
35dd052c93 LethalDamage: update Property when PT or counter changes 2020-05-08 03:38:44 +00:00
Michael Kamensky
09d2e1fb30 Merge branch 'castSALastZoneFix' into 'master'
CastSA: fix LastZone use LKI copy when put on stack

See merge request core-developers/forge!2790
2020-05-08 03:38:11 +00:00
Hans Mackowiak
cc10e91431 CastSA: fix LastZone use LKI copy when put on stack 2020-05-08 03:38:11 +00:00
friarsol
8ab0857750 Add Companion keyword 2020-05-07 22:54:15 -04:00
Andreas Bendel
21989c31a5 Update de-DE.properties 2020-05-06 20:57:06 +00:00
Michael Kamensky
f651cd42f4 Merge branch 'master' into 'master'
Preparing Forge for Android publish 1.6.34.001 [incremental].

See merge request core-developers/forge!2788
2020-05-06 10:50:47 +00:00
Agetian
b628b8ca00 - Preparing Forge for Android publish 1.6.34.001 [incremental]. 2020-05-06 13:17:07 +03:00
Blacksmith
3534f1ec05 Clear out release files in preparation for next release 2020-05-05 16:12:14 +00:00
Blacksmith
98d80a1328 [maven-release-plugin] prepare for next development iteration 2020-05-05 16:05:39 +00:00
Blacksmith
2d3a538f6d [maven-release-plugin] prepare release forge-1.6.34 2020-05-05 16:05:31 +00:00
Blacksmith
573902f51e Update README.txt for release 2020-05-05 16:01:55 +00:00
Sol
47fd6734ab Merge branch 'inputManaCacheFix' into 'master'
InputPayMana: fix ManaInput Dialog using getAbilityToPlay

See merge request core-developers/forge!2787
2020-05-05 01:57:31 +00:00
Hans Mackowiak
4047dfe727 InputPayMana: fix ManaInput Dialog using getAbilityToPlay 2020-05-05 01:57:31 +00:00
Michael Kamensky
64085463d8 Merge branch 'damageTrample' into 'master'
DealDamage: add ExcessDamage

See merge request core-developers/forge!2784
2020-05-04 14:04:02 +00:00
Hans Mackowiak
18154ca0b7 DealDamage: add ExcessDamage 2020-05-04 14:04:02 +00:00
swordshine
0fcbc62801 Merge branch 'eachplyrcounters' into 'master'
Placing counters fixes

See merge request core-developers/forge!2785
2020-05-04 02:12:04 +00:00
Tim Mocny
84455ba61d Placing counters fixes 2020-05-04 02:12:04 +00:00
Michael Kamensky
7c27efbd64 Merge branch 'newBranch' into 'master'
Update Portrait Layout (Mobile)

See merge request core-developers/forge!2781
2020-05-03 19:27:46 +00:00
Michael Kamensky
6246b16372 Merge branch 'patch-5' into 'master'
Update cazur_ruthless_stalker.txt

See merge request core-developers/forge!2783
2020-05-03 17:50:58 +00:00
Tim Mocny
f497f75c3d Update cazur_ruthless_stalker.txt 2020-05-03 17:13:34 +00:00
Anthony Calosa
4a957c240a Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-05-03 18:35:46 +08:00
Michael Kamensky
7910a37902 Merge branch 'delayedPutCounterFix' into 'master'
cards: some fixes with delayed trigger and putcounter

See merge request core-developers/forge!2782
2020-05-03 10:13:06 +00:00
Hans Mackowiak
3b6160cd88 cards: some fixes with delayed trigger and putcounter 2020-05-03 12:06:08 +02:00
Anthony Calosa
ae545ed169 Update Portrait Layout
Fixes team button visibility and deck button text readability.
2020-05-03 17:55:16 +08:00
Michael Kamensky
4406c7ba72 Merge branch 'evenOdd' into 'master'
add ChooseEvenOdd

See merge request core-developers/forge!2780
2020-05-03 04:55:13 +00:00
Hans Mackowiak
b964cc6448 add ChooseEvenOdd 2020-05-03 04:55:13 +00:00
Michael Kamensky
ca4687f79a Merge branch 'fix' into 'master'
rielle_the_everwise.txt static fix

See merge request core-developers/forge!2779
2020-05-02 16:37:34 +00:00
Tim Mocny
42daf6b5ee rielle_the_everwise.txt static fix 2020-05-02 16:37:34 +00:00
Michael Kamensky
f696a5488c Merge branch 'iko-release' into 'master'
Iko release

See merge request core-developers/forge!2778
2020-05-01 04:36:29 +00:00
Michael Kamensky
853aad6e65 Merge branch 'huntedNightmareAI' into 'master'
Hunted Nightmare and AI fix

See merge request core-developers/forge!2777
2020-05-01 04:26:03 +00:00
Hans Mackowiak
3fe1aa45a2 Hunted Nightmare and AI fix 2020-05-01 04:26:03 +00:00
friarsol
d8ebc3b5f7 Migrate C2020 2020-04-30 21:36:36 -04:00
friarsol
23f35854ba Migrate upcoming IKO cards 2020-04-30 21:32:07 -04:00
friarsol
5cecbe63b1 Update release files 2020-04-30 21:29:50 -04:00
Michael Kamensky
ae6e290a33 Merge branch 'triggerDiscardedAll' into 'master'
TriggerDiscardAll: new trigger for Rielle, the Everwise

See merge request core-developers/forge!2776
2020-04-30 09:50:55 +00:00
Hans Mackowiak
eb0a30f013 TriggerDiscardAll: new trigger for Rielle, the Everwise 2020-04-30 09:50:55 +00:00
Michael Kamensky
be1574b44d Merge branch 'newBranch' into 'master'
Fix Card Display on Network Client

Closes #159

See merge request core-developers/forge!2775
2020-04-30 04:19:56 +00:00
Anthony Calosa
c5aa8cfae7 Fix Card Display on Network Client
Adventure, Flip, Transform, Manifest, Morph etc display properly on Client Side (fix #159)
Also Fix Aura Display

I don't encounter issue #512 anyomre but I leave it open (limited testing)
2020-04-30 06:20:05 +08:00
Michael Kamensky
2e3e3537fb Merge branch '1387-npe-in-triggerdamagedealtonce-getdamagetargets-triggerdamagedealtonce-java-126' into 'master'
Resolve "NPE in TriggerDamageDealtOnce.getDamageTargets(TriggerDamageDealtOnce.java:126)"

Closes #1387

See merge request core-developers/forge!2774
2020-04-29 08:02:31 +00:00
Hans Mackowiak
39378e9776 Trigger: fix NPE if ValidSource or ValidTarget is missing for damage trigger 2020-04-29 08:53:31 +02:00
swordshine
6a73a990db Merge branch 'gzlands' into 'master'
SLD - Godzilla lands

See merge request core-developers/forge!2773
2020-04-29 01:03:12 +00:00
Tim Mocny
be73f20ce0 SLD - Godzilla lands 2020-04-29 01:03:12 +00:00
Michael Kamensky
44e411f925 Merge branch 'fixCastSA' into 'master'
GameAction: hotfix for CastSA

See merge request core-developers/forge!2772
2020-04-28 17:09:35 +00:00
Hans Mackowiak
c7f0def063 GameAction: hotfix for CastSA 2020-04-28 17:09:35 +00:00
CCTV-1
986a273772 update translation 2020-04-28 16:23:28 +08:00
CCTV-1
6a7bfd48bf translate new menu item 2020-04-28 16:23:12 +08:00
Michael Kamensky
4e6addcf84 Merge branch 'damageMultipleAmount' into 'master'
DamageMap: TriggerDamageDealtOnce and TriggerDamageDoneOnce now filter the amount

Closes #1382

See merge request core-developers/forge!2771
2020-04-28 08:22:20 +00:00
CCTV-1
5a3db18220 Merge remote-tracking branch 'upstream/master' 2020-04-28 16:21:04 +08:00
Hans Mackowiak
2df5373902 DamageMap: TriggerDamageDealtOnce and TriggerDamageDoneOnce now filter the amount 2020-04-28 08:09:59 +02:00
Michael Kamensky
3a53ab8a9c Merge branch 'lastStateLandFix' into 'master'
Last state land fix

Closes #1386

See merge request core-developers/forge!2770
2020-04-28 05:24:50 +00:00
Hans Mackowiak
1ea93a6f2e Last state land fix 2020-04-28 05:24:49 +00:00
swordshine
970c3451e9 Merge branch 'bans' into 'master'
April 2020 Bans

See merge request core-developers/forge!2769
2020-04-28 00:38:08 +00:00
Tim Mocny
0960ed98f0 April 2020 Bans 2020-04-28 00:38:08 +00:00
swordshine
4701371b17 Merge branch 'raid' into 'master'
Raid wording change

Closes #1357

See merge request core-developers/forge!2768
2020-04-28 00:37:54 +00:00
Tim Mocny
11c5b00130 Raid wording change 2020-04-28 00:37:54 +00:00
Michael Kamensky
e0f2d09dfd Merge branch 'master' into 'master'
Fix an issue with imprinting not working correctly in game states after the first game.

Closes #1383

See merge request core-developers/forge!2766
2020-04-27 17:23:59 +00:00
Agetian
2d355d4ea3 - Fix an issue with imprinting not working correctly in game states after the first game. 2020-04-27 20:18:35 +03:00
Michael Kamensky
bea46a7a8c Merge branch 'fixes' into 'master'
Fixes - 27 April

See merge request core-developers/forge!2765
2020-04-27 17:13:07 +00:00
Tim Mocny
d13439500f Fixes - 27 April 2020-04-27 17:13:07 +00:00
Michael Kamensky
bd54bbe026 Merge branch 'patch' into 'master'
Add The Ozolith

See merge request core-developers/forge!2764
2020-04-27 15:54:35 +00:00
Michael Kamensky
15f147ac9c Merge branch 'removeSpellAbilityCache' into 'master'
Game: remove SpellAbilityCache

See merge request core-developers/forge!2763
2020-04-27 10:32:17 +00:00
Hans Mackowiak
c2248ef905 Game: remove SpellAbilityCache 2020-04-27 10:32:17 +00:00
swordshine
74c92590fc Add The Ozolith 2020-04-27 16:16:11 +08:00
Michael Kamensky
552a7130a5 Merge branch '1380-selective-adaptation-triggers-excessive-memory-usage' into 'master'
Resolve "'Selective Adaptation' triggers excessive memory usage"

Closes #1380

See merge request core-developers/forge!2762
2020-04-26 15:54:16 +00:00
Hans Mackowiak
8d1c02012e Resolve "'Selective Adaptation' triggers excessive memory usage" 2020-04-26 15:54:15 +00:00
Michael Kamensky
c4963821e5 Merge branch 'raptorBlockedFix' into 'master'
fix Labyrinth Raptor

Closes #1375

See merge request core-developers/forge!2761
2020-04-26 15:15:53 +00:00
Michael Kamensky
8a648c9ccf Merge branch 'newBranch' into 'master'
Update Win/Lose Overlay (mobile)

Closes #1367 and #928

See merge request core-developers/forge!2760
2020-04-26 15:15:48 +00:00
Hans Mackowiak
a2b5bb911c fix Labyrinth Raptor 2020-04-26 16:57:54 +02:00
Anthony Calosa
e7cbee211d Update controls 2020-04-26 18:37:10 +08:00
Anthony Calosa
a553103e39 AI vs AI match shows Match Results (mobile) 2020-04-26 17:25:44 +08:00
Anthony Calosa
45a5e76e63 refactor CardAreaPanel.resetForNewGame
fix issue ##1367
2020-04-26 16:44:32 +08:00
Anthony Calosa
8122589842 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-26 15:26:14 +08:00
Anthony Calosa
d754c2c4d3 Update Win/Lose Overlay (mobile)
At the end of the match, user can minimize the overlay to see the last battlefield and browse other zones, to bring up the overlay, click game menu, and click Show WinLose Overlay.

Also fixes issue #928
2020-04-26 15:00:57 +08:00
swordshine
7e9843aae5 Merge branch 'heliodsp' into 'master'
Heliod's Punishment stack cleanup (ORIGINALHOST stack issue)

See merge request core-developers/forge!2647
2020-04-26 03:46:06 +00:00
Tim Mocny
2897d4304d Heliod's Punishment stack cleanup (ORIGINALHOST stack issue) 2020-04-26 03:46:06 +00:00
swordshine
f5400d18bd Merge branch 'extinctionfix' into 'master'
extinction_event.txt - fix

Closes #1377

See merge request core-developers/forge!2759
2020-04-26 00:48:39 +00:00
Tim Mocny
46983de66f extinction_event.txt - fix 2020-04-26 00:48:39 +00:00
Michael Kamensky
049e129606 Merge branch 'fixXPaidZeroCyclingCastTrigger' into 'master'
fix Cycling for Zero with xPaid, also do Zaxara with xPaid

See merge request core-developers/forge!2758
2020-04-25 19:56:37 +00:00
Hans Mackowiak
b5deb0b252 fix Cycling for Zero with xPaid, also do Zaxara with xPaid 2020-04-25 19:56:37 +00:00
Michael Kamensky
0bb42a2794 Merge branch '1372-used-memory-grows-quadratic-in-the-number-of-triggered-spells' into 'master'
Resolve "Used memory grows quadratic in the number of triggered spells"

Closes #1372

See merge request core-developers/forge!2756
2020-04-25 17:36:50 +00:00
Hans Mackowiak
a9b5e1d112 Resolve "Used memory grows quadratic in the number of triggered spells" 2020-04-25 17:36:50 +00:00
Michael Kamensky
c1c9271458 Merge branch 'master' into 'master'
Add color requirement AI hints to the ELD land cycle that usually comes tapped.

See merge request core-developers/forge!2757
2020-04-25 15:52:43 +00:00
Agetian
44e4dfecca - Add color requirement AI hints to the ELD land cycle that usually comes tapped and has a "comes untapped" effect. 2020-04-25 18:38:36 +03:00
swordshine
d809fa6b91 Merge branch 'mysticsubdual' into 'master'
IKO - mystic_subdual.txt

See merge request core-developers/forge!2754
2020-04-25 04:47:10 +00:00
Tim Mocny
c5a89747b8 IKO - mystic_subdual.txt 2020-04-25 04:47:10 +00:00
Michael Kamensky
36341a85cd Merge branch 'move' into 'master'
Tweaks - 24 April

See merge request core-developers/forge!2753
2020-04-24 14:34:22 +00:00
Tim Mocny
253256799c Tweaks - 24 April 2020-04-24 14:34:22 +00:00
Michael Kamensky
16fa79132b Merge branch 'zaxara' into 'master'
Zaxara (C20)

See merge request core-developers/forge!2735
2020-04-24 14:33:18 +00:00
Tim Mocny
f02ab6c073 Zaxara (C20) 2020-04-24 14:33:18 +00:00
swordshine
ef208f3442 Merge branch 'fixes.4.23' into 'master'
Fixes - 23 April

See merge request core-developers/forge!2752
2020-04-24 00:23:42 +00:00
Northmoc
bdcaf785bd labyrinth_raptor.txt - ChangeNum$ -> Amount$ 2020-04-23 19:14:54 -04:00
Northmoc
318c4891a4 grave_betrayal.txt - RememberObjects$ TriggeredCard 2020-04-23 19:14:54 -04:00
Michael Kamensky
a6fcec519d Merge branch 'master' into 'master'
Added an AI hint for Leyline of Abundance.

See merge request core-developers/forge!2750
2020-04-23 15:33:19 +00:00
Agetian
7f3bc8f7f9 - Added an AI hint for Leyline of Abundance. 2020-04-23 18:09:42 +03:00
Michael Kamensky
f2e7068706 Merge branch 'titansnest' into 'master'
Titans' Nest (IKO)

See merge request core-developers/forge!2708
2020-04-23 15:00:05 +00:00
Tim Mocny
67c0722db8 Titans' Nest (IKO) 2020-04-23 15:00:05 +00:00
Michael Kamensky
60ad4cf0d1 Merge branch 'master' into 'master'
Added a couple AI hints.

See merge request core-developers/forge!2749
2020-04-23 09:43:14 +00:00
Agetian
d76004509d - Added a couple AI hints. 2020-04-23 11:02:51 +03:00
Michael Kamensky
bac8497bc3 Merge branch 'newBranch' into 'master'
Fix uncleared selectables when a player concedes and restarts next game

See merge request core-developers/forge!2748
2020-04-23 06:54:33 +00:00
Anthony Calosa
ac9636c44e Fix uncleared selectables when a player concedes and restarts next game 2020-04-23 14:07:15 +08:00
Michael Kamensky
89b534a580 Merge branch 'master' into 'master'
Add basic AI logic for Capricopian.

See merge request core-developers/forge!2747
2020-04-22 18:16:28 +00:00
Agetian
daa982389e - Add basic logic for Capricopian. 2020-04-22 21:05:37 +03:00
Michael Kamensky
e56ee38933 Merge branch 'capricopian' into 'master'
Capricopian (C20)

See merge request core-developers/forge!2742
2020-04-22 17:12:23 +00:00
Tim Mocny
b12ed3307a Capricopian (C20) 2020-04-22 17:12:23 +00:00
Michael Kamensky
08446a344c Merge branch 'master' into 'master'
Add an attack effect hint for Krenko, Tin Street Kingpin.

See merge request core-developers/forge!2746
2020-04-22 16:42:47 +00:00
Agetian
5ec6589175 - Add an attack effect hint for Krenko, Tin Street Kingpin. 2020-04-22 19:30:28 +03:00
Michael Kamensky
bdff4a12e7 Merge branch 'master' into 'master'
Run the AI trigger code in case the SA is pre-targeted earlier in the code (fixes Nyx Herald) + improve AI Adventure support

See merge request core-developers/forge!2745
2020-04-22 16:25:43 +00:00
Agetian
f610203a53 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2020-04-22 19:19:53 +03:00
Agetian
37b7408163 - Improve support for AI casting Sorcery speed Adventure cards. 2020-04-22 19:16:30 +03:00
Agetian
67684fa4ec - Don't cut short when checking whether to run the AI trigger code if the triggered ability was pre-targeted in earlier checks before doTrigger is called. 2020-04-22 18:43:22 +03:00
Michael Kamensky
5787bf7660 Merge branch 'xyris' into 'master'
Xyris (C20)

See merge request core-developers/forge!2734
2020-04-22 15:13:31 +00:00
Tim Mocny
e621972ad2 Xyris (C20) 2020-04-22 15:13:30 +00:00
Michael Kamensky
84146df00e Merge branch 'manascape' into 'master'
Manascape Refractor (C20)

See merge request core-developers/forge!2737
2020-04-22 14:57:46 +00:00
Northmoc
a27673ef5b manascape_refractor.txt 2020-04-22 10:46:40 -04:00
Michael Kamensky
4c8072afd2 Merge branch 'master' into 'master'
Added several new cubes ported from XMage + two submitted cubes

See merge request core-developers/forge!2744
2020-04-22 08:09:34 +00:00
Agetian
6340f92c95 - Added several new cubes ported from XMage + two cubes submitted on Slightlymagic (by thegreatone1357 and dolphi5). 2020-04-22 11:02:28 +03:00
Michael Kamensky
9349477f21 Merge branch 'newBranch' into 'master'
Improve Online Lobby (Mobile Port)

Closes #659, #658, #172, and #914

See merge request core-developers/forge!2738
2020-04-22 04:24:19 +00:00
Anthony Calosa
96e243e4c9 Refactor stack -> Deque, add check for transformCount 2020-04-22 07:29:49 +08:00
Anthony Calosa
828885c9b0 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-22 05:55:38 +08:00
Michael Kamensky
4694ea8014 Merge branch 'mutual_destruction' into 'master'
Mutual Destruction fix :: AffectedZones -> EffectZone

See merge request core-developers/forge!2743
2020-04-21 18:29:05 +00:00
Tim Mocny
41d0fa9227 Mutual Destruction fix :: AffectedZones -> EffectZone 2020-04-21 18:29:05 +00:00
Michael Kamensky
7e158620f3 Merge branch 'nikara_yannik' into 'master'
Nikara and Yannik (C20)

See merge request core-developers/forge!2736
2020-04-21 18:28:38 +00:00
Tim Mocny
4ffbaf87a0 Nikara and Yannik (C20) 2020-04-21 18:28:37 +00:00
Michael Kamensky
0a32dcffa6 Merge branch 'cleanup' into 'master'
Cleanups - 21 April

See merge request core-developers/forge!2741
2020-04-21 17:06:24 +00:00
Tim Mocny
f20f4dcfe4 Cleanups - 21 April 2020-04-21 17:06:24 +00:00
Michael Kamensky
377e2dc9a6 Merge branch 'patch-2' into 'master'
Update alpine_moon.txt

See merge request core-developers/forge!2740
2020-04-21 16:56:01 +00:00
Michael Kamensky
a25404d029 Merge branch 'whenyoudo' into 'master'
Fixing triggers for "When you do" clause

See merge request core-developers/forge!2739
2020-04-21 16:55:53 +00:00
Tim Mocny
838c4daf25 Fixing triggers for "When you do" clause 2020-04-21 16:55:53 +00:00
Michael Kamensky
ef8533237e Merge branch 'commandertohand' into 'master'
Netherborn Altar (C20) and Command Beacon update

See merge request core-developers/forge!2732
2020-04-21 16:52:30 +00:00
Tim Mocny
30cdf13d88 Netherborn Altar (C20) and Command Beacon update 2020-04-21 16:52:30 +00:00
Michael Kamensky
e739894759 Merge branch 'c20.20' into 'master'
C20 - 20 April

See merge request core-developers/forge!2733
2020-04-21 16:51:18 +00:00
Tim Mocny
dfd07d1d12 C20 - 20 April 2020-04-21 16:51:18 +00:00
Computica
3ae3ec003e Update alpine_moon.txt
"AffectedZones$ All" to "AffectedZone$ All"
2020-04-21 15:55:04 +00:00
Anthony Calosa
f68ac2e43a refactor stack -> deque 2020-04-21 20:31:41 +08:00
Anthony Calosa
8acb143fea Improve Online Lobby (Mobile port) 2020-04-21 17:52:05 +08:00
Anthony Calosa
3cdbf3eb67 Update FControlGameEventHandler
some method don't exist on mobile port and are specifically for desktop so let those routine run for its specific platform
2020-04-21 17:50:25 +08:00
Michael Kamensky
1f7faa773c Merge branch 'gavi' into 'master'
Gavi, Nest Warden

See merge request core-developers/forge!2656
2020-04-20 12:05:33 +00:00
Tim Mocny
f27748eeca Gavi, Nest Warden 2020-04-20 12:05:32 +00:00
Michael Kamensky
789019fff1 Merge branch 'c20.19s' into 'master'
C20 - 19 April (Svaldan)

See merge request core-developers/forge!2727
2020-04-20 07:54:05 +00:00
Tim Mocny
88d768f8b7 C20 - 19 April (Svaldan) 2020-04-20 07:54:04 +00:00
Michael Kamensky
e2f7faa4d7 Merge branch 'drannithfix' into 'master'
Drannith Magistrate - stop command zone casting

See merge request core-developers/forge!2730
2020-04-20 07:49:08 +00:00
Tim Mocny
344792d91c Drannith Magistrate - stop command zone casting 2020-04-20 07:49:08 +00:00
Michael Kamensky
879322d3b0 Merge branch 'c20.19' into 'master'
C20 - 19 April

See merge request core-developers/forge!2728
2020-04-20 07:43:45 +00:00
Tim Mocny
e9a93a0f8d C20 - 19 April 2020-04-20 07:43:45 +00:00
Michael Kamensky
a9482c349e Merge branch 'selective_adaptation' into 'master'
Selective Adaption - Causing lag?

See merge request core-developers/forge!2731
2020-04-20 04:52:44 +00:00
Tim Mocny
2c8bc0b582 Selective Adaption - Causing lag? 2020-04-20 04:52:44 +00:00
Michael Kamensky
62ba27d09f Merge branch 'fireflux_squad' into 'master'
Fireflux Squad (C20)

See merge request core-developers/forge!2723
2020-04-20 04:15:20 +00:00
Tim Mocny
d6efcee188 Fireflux Squad (C20) 2020-04-20 04:15:19 +00:00
Michael Kamensky
4744f11e69 Merge branch 'newBranch' into 'master'
Update KeywordCard, add Flash Icon, try to prevent rotate bug

See merge request core-developers/forge!2715
2020-04-19 16:28:04 +00:00
Michael Kamensky
20549fe0ca Merge branch 'martial_impetus' into 'master'
Martial Impetus and Frontier Warmonger (C20)

See merge request core-developers/forge!2722
2020-04-19 16:07:30 +00:00
Tim Mocny
8c023fbfea Martial Impetus and Frontier Warmonger (C20) 2020-04-19 16:07:29 +00:00
Michael Kamensky
158711f7a9 Merge branch 'c20.17' into 'master'
C20 - 17 April

See merge request core-developers/forge!2721
2020-04-19 16:03:33 +00:00
Tim Mocny
5be2ab9358 C20 - 17 April 2020-04-19 16:03:33 +00:00
Anthony Calosa
99f07c1471 update changes 2020-04-19 21:16:23 +08:00
Michael Kamensky
f7b1a24fdd Merge branch 'c20.18' into 'master'
C20 - 18 April

See merge request core-developers/forge!2724
2020-04-19 05:11:09 +00:00
Tim Mocny
610e0e546a C20 - 18 April 2020-04-19 05:11:09 +00:00
Michael Kamensky
eb02f4acec Merge branch 'cyclingIKO' into 'master'
Cycling: add YouCycledThisTurn, OnlyFirst for Trigger and ValidCause for ReplaceDraw

Closes #1344

See merge request core-developers/forge!2725
2020-04-19 03:24:10 +00:00
Hans Mackowiak
ef95f02fa2 Cycling: add YouCycledThisTurn, OnlyFirst for Trigger and ValidCause for ReplaceDraw 2020-04-19 03:24:10 +00:00
Sol
6b38bc4b7f Merge branch 'update-formats' into 'master'
Update IKO into Format files

See merge request core-developers/forge!2726
2020-04-19 00:39:34 +00:00
friarsol
57e788a69d Update IKO into Format files 2020-04-18 20:29:06 -04:00
Anthony Calosa
e2c28897d1 update additional 2020-04-18 22:13:24 +08:00
Anthony Calosa
34cb7ff9de updated code 2020-04-18 20:57:18 +08:00
Anthony Calosa
07123286aa add reset (on libgdx discord they said before transforming, you need to reset) 2020-04-18 19:39:57 +08:00
Anthony Calosa
017f9f1984 revert previous attempt to fix the rotate bug 2020-04-18 18:11:27 +08:00
Anthony Calosa
06b9143d57 update endtransform 2020-04-18 17:43:13 +08:00
Anthony Calosa
b1e9f4a110 startRotateTransform 2020-04-18 16:14:49 +08:00
Anthony Calosa
d24fa32175 add flash indicator for desktop 2020-04-18 05:57:17 +08:00
Anthony Calosa
1f7dade4f9 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-18 05:56:27 +08:00
Michael Kamensky
58e99ca69b Merge branch 'iko16' into 'master'
IKO edition - Yidaro rarity fix

See merge request core-developers/forge!2720
2020-04-17 15:05:11 +00:00
Tim Mocny
ca43cf8934 IKO edition - Yidaro rarity fix 2020-04-17 15:05:11 +00:00
Michael Kamensky
56d14d25eb Merge branch 'winota' into 'master'
★Winota

See merge request core-developers/forge!2707
2020-04-17 13:08:58 +00:00
Tim Mocny
bf548096f1 ★Winota 2020-04-17 13:08:57 +00:00
Michael Kamensky
e53e9a5bdf Merge branch 'iko14a' into 'master'
Extinction Event

See merge request core-developers/forge!2711
2020-04-17 11:53:34 +00:00
Northmoc
64f1ad9778 extinction_event.txt 2020-04-17 07:43:34 -04:00
Michael Kamensky
c3621cc8e6 Merge branch 'master' into 'master'
Added Ikoria planeswalker achievements by Marek.

See merge request core-developers/forge!2719
2020-04-17 06:32:53 +00:00
Agetian
250f4484ce - Added Ikoria planeswalker achievements by Marek. 2020-04-17 08:11:36 +03:00
Michael Kamensky
4d65ab429d Merge branch 'kathril' into 'master'
Kathril, Aspect Warper

See merge request core-developers/forge!2718
2020-04-17 03:54:54 +00:00
Tim Mocny
2cfb4917b1 Kathril, Aspect Warper 2020-04-17 03:54:54 +00:00
Michael Kamensky
4bcb516abd Merge branch 'ikoc20translation' into 'master'
Add IKO and C20 Card Translations

See merge request core-developers/forge!2717
2020-04-17 03:54:46 +00:00
Anthony Calosa
29c4d6d4d1 limit rotate transform to overlay and child objects 2020-04-17 06:54:14 +08:00
Michael Kamensky
a7b3d18f66 Merge branch 'c20.14' into 'master'
C20 - 14 April

See merge request core-developers/forge!2712
2020-04-16 16:46:01 +00:00
Tim Mocny
32256a398a C20 - 14 April 2020-04-16 16:46:01 +00:00
klaxnek
14e6baf455 Add IKO and C20 Card Translations
Fixed cardnamesTranslations.py to support adding new patched cards.
2020-04-16 18:19:59 +02:00
klaxnek
24e7cc7999 Merge remote-tracking branch 'core-developers/master' 2020-04-16 17:57:58 +02:00
Sol
210a6637bb Merge branch 'iko14b' into 'master'
IKO edition/booster update

See merge request core-developers/forge!2713
2020-04-16 01:13:51 +00:00
Tim Mocny
23688b281b IKO edition/booster update 2020-04-16 01:13:51 +00:00
Anthony Calosa
f968e310f2 allow startRotateTransform for child FdisplayObject 2020-04-16 08:09:22 +08:00
Anthony Calosa
afc55d4b2e Refactor updatePhase 2020-04-16 07:43:03 +08:00
Anthony Calosa
ebd0e12bee Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-15 17:48:41 +08:00
Anthony Calosa
bd6a1f3a2b Update 2020-04-15 16:46:24 +08:00
Michael Kamensky
ca6873d893 Merge branch 'fixTriggerNullAi' into 'master'
WrappedAbility: fix trigger not set for confirmTrigger

See merge request core-developers/forge!2716
2020-04-15 08:13:16 +00:00
Hans Mackowiak
759703bf56 WrappedAbility: fix trigger not set for confirmTrigger 2020-04-15 08:13:16 +00:00
Hans Mackowiak
d52d850c85 Merge branch 'callofdd' into 'master'
★Call of the Death-Dweller

See merge request core-developers/forge!2673
2020-04-15 07:16:28 +00:00
Tim Mocny
475ae289fd ★Call of the Death-Dweller 2020-04-15 07:16:27 +00:00
CCTV-1
ac6b983cb5 update translations 2020-04-15 14:05:21 +08:00
CCTV-1
1f4f7fc7f4 translate mobile online screen 2020-04-15 14:05:05 +08:00
CCTV-1
e85ed216be Merge remote-tracking branch 'upstream/master' 2020-04-15 13:58:10 +08:00
Anthony Calosa
427e34607d Prevent EmptyStackException 2020-04-15 13:43:58 +08:00
Anthony Calosa
2778318b10 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-15 12:49:58 +08:00
Anthony Calosa
102119679c Update Flash Icons
Add a helper icon for cards with flash or can be cast as though it had flash (if show ability icons is enabled)
2020-04-15 12:33:31 +08:00
Anthony Calosa
5467701fa0 Update Keyword Effects
Whenever a keyword is added/removed to the player, it will update the keyword card, and will auto remove it if its empty
2020-04-15 12:29:55 +08:00
Sol
d6dc081e42 Merge branch 'newBranch' into 'master'
Network Improvements

See merge request core-developers/forge!2701
2020-04-15 02:40:18 +00:00
Anthony Calosa
d87d825bce Network Improvements 2020-04-15 02:40:18 +00:00
Anthony Calosa
d4625864df Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-15 07:39:35 +08:00
Michael Kamensky
7ca5eee57e Merge branch 'iko13' into 'master'
IKO - 13 April

See merge request core-developers/forge!2704
2020-04-14 17:25:43 +00:00
Tim Mocny
38d708ce65 IKO - 13 April 2020-04-14 17:25:43 +00:00
Michael Kamensky
c6f1dc6bd1 Merge branch 'imagekeyrobustness' into 'master'
Alternative image lookup to avoid issues with multiple printings in a single...

See merge request core-developers/forge!2705
2020-04-14 06:27:07 +00:00
Michael Kamensky
aeb0bfc971 Merge branch 'counterPutChooser' into 'master'
CounterPutEffect: add Choicer Param

See merge request core-developers/forge!2709
2020-04-14 06:25:53 +00:00
Hans Mackowiak
f1690f2d84 CounterPutEffect: add Choicer Param 2020-04-14 06:25:53 +00:00
Anthony Calosa
e20c79fa4b Update 2020-04-14 10:10:53 +08:00
austinio7116
f6201d178e Alternative image lookup to avoid issues with multiple printings in a single edition - e.g. supporting loading *1.full.jpg if only .full.jpg is present, or loading the standard card if the promo is missing. 2020-04-13 17:47:30 +01:00
Anthony Calosa
17b2a2b023 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-13 22:21:25 +08:00
austinio7116
d17015cc88 Merge branch 'patch-5' into 'master'
Update ImageKeys.java to support .full images first art variant renaming optional

See merge request core-developers/forge!2703
2020-04-13 13:55:17 +00:00
Anthony Calosa
5649ac6d7d Update ImageKeys.java 2020-04-13 12:55:13 +00:00
Anthony Calosa
d1cc547239 Update ImageKeys.java to support .full images first art variant renaming optional 2020-04-13 12:51:23 +00:00
Michael Kamensky
ce1aca2674 Merge branch 'fixMaxTotalTargetCMC' into 'master'
SpellAbility: fix MaxTotalTargetCMC

Closes #1346

See merge request core-developers/forge!2702
2020-04-13 11:57:43 +00:00
Hans Mackowiak
f3729d0059 SpellAbility: fix MaxTotalTargetCMC 2020-04-13 11:57:43 +00:00
Anthony Calosa
d6c5d7f04d Network Improvements 2020-04-13 15:42:56 +08:00
Hans Mackowiak
fdd009908d Merge branch 'mythos2' into 'master'
IKO - Mythos cycle tweaks

See merge request core-developers/forge!2697
2020-04-13 04:42:16 +00:00
Tim Mocny
4be26f575c IKO - Mythos cycle tweaks 2020-04-13 04:42:16 +00:00
Sol
84519d10ea Merge branch 'iko12' into 'master'
IKO - 12 April

See merge request core-developers/forge!2700
2020-04-13 01:26:11 +00:00
Tim Mocny
380a2649d0 IKO - 12 April 2020-04-13 01:26:11 +00:00
Sol
e555c577de Merge branch 'iko11' into 'master'
IKO - 11 April

See merge request core-developers/forge!2694
2020-04-13 01:08:46 +00:00
Tim Mocny
377c0b6a9a IKO - 11 April 2020-04-13 01:08:45 +00:00
Sol
63f0df433f Merge branch 'ikoeditionsupdate' into 'master'
More complete IKO editions file - probably not final, but parsed from scryfall

See merge request core-developers/forge!2696
2020-04-13 00:56:40 +00:00
Sol
1fb56331f9 Merge branch 'typos' into 'master'
More typos

See merge request core-developers/forge!2699
2020-04-13 00:55:34 +00:00
Tim Mocny
5a348f06cd More typos 2020-04-13 00:55:34 +00:00
austinio7116
d869d6b43f Corrected edition type 2020-04-12 18:05:46 +01:00
Michael Kamensky
6e9827719b Merge branch 'spellAbilityXPaid' into 'master'
SpellAbility: do xPaid inside spellAbility

See merge request core-developers/forge!2698
2020-04-12 15:19:04 +00:00
Hans Mackowiak
0d1105377e SpellAbility: do xPaid inside spellAbility 2020-04-12 15:19:04 +00:00
Michael Kamensky
9b9f88129f Merge branch 'iko10' into 'master'
IKO - 10 April

See merge request core-developers/forge!2687
2020-04-12 15:07:02 +00:00
Tim Mocny
d7d59d64b8 IKO - 10 April 2020-04-12 15:07:01 +00:00
austinio7116
015e51d668 More complete IKO editions file - probably not final, but parsed from scryfall 2020-04-12 12:32:21 +01:00
Sol
8f8fca8d80 Merge branch 'tormods' into 'master'
Tormod's Crypt fix

See merge request core-developers/forge!2691
2020-04-11 23:50:49 +00:00
Tim Mocny
81d8c42f29 Tormod's Crypt fix 2020-04-11 23:50:49 +00:00
Sol
630b9d3dd7 Merge branch 'counters' into 'master'
IKO - Counters revisions

See merge request core-developers/forge!2692
2020-04-11 23:50:29 +00:00
Tim Mocny
0db2e0d95b IKO - Counters revisions 2020-04-11 23:50:29 +00:00
Sol
0be5a1180f Merge branch 'newBranch' into 'master'
Fix chooseEntitiesForEffect, Fix Scry Crash (Desktop vs Mobile)

See merge request core-developers/forge!2688
2020-04-11 23:46:50 +00:00
Sol
079ad0223a Merge branch 'typos' into 'master'
Typos

See merge request core-developers/forge!2690
2020-04-11 23:44:54 +00:00
Tim Mocny
bcdc3c849a Typos 2020-04-11 23:44:54 +00:00
Hans Mackowiak
cb7d879abb Merge branch '1358-choosegenericeffect-atrandom-param-update-needed' into 'master'
Resolve "ChooseGenericEffect AtRandom param update needed"

Closes #1358

See merge request core-developers/forge!2693
2020-04-11 20:48:04 +00:00
Hans Mackowiak
a0ea1f1d3f ChooseGenericEffect: when using AtRandom with IsPresent Requirement add check when no choices left 2020-04-11 20:08:18 +02:00
Michael Kamensky
b4cc8421c5 Merge branch 'mythosCycle' into 'master'
IKO: Mythos cycle

See merge request core-developers/forge!2689
2020-04-11 10:21:38 +00:00
Hans Mackowiak
1ac8026b7f IKO: Mythos cycle 2020-04-11 10:21:38 +00:00
Anthony Calosa
f68e3dee71 Fix crash for scry (desktop vs android) match 2020-04-11 17:44:30 +08:00
Anthony Calosa
7abbab5f42 Fix chooseEntitiesForEffect (Cards like Once Upon a Time, etc.. using this will work on network) 2020-04-11 15:06:00 +08:00
Michael Kamensky
9033bbd410 Merge branch 'oracle' into 'master'
Oracle changes - April 10

See merge request core-developers/forge!2685
2020-04-11 04:43:01 +00:00
Tim Mocny
8d96dd99fa Oracle changes - April 10 2020-04-11 04:43:00 +00:00
Michael Kamensky
e327c74976 Merge branch 'otters' into 'master'
205.3 subtype lists - Lukka and Otter

See merge request core-developers/forge!2686
2020-04-11 04:42:45 +00:00
Tim Mocny
4319111c55 205.3 subtype lists - Lukka and Otter 2020-04-11 04:42:45 +00:00
Michael Kamensky
efaac25920 Merge branch 'c20.9' into 'master'
C20 - 9 April

See merge request core-developers/forge!2683
2020-04-11 04:40:39 +00:00
Tim Mocny
82ffc4e9d5 C20 - 9 April 2020-04-11 04:40:38 +00:00
Michael Kamensky
bda8c00e13 Merge branch 'iko9' into 'master'
IKO - 9 April

See merge request core-developers/forge!2682
2020-04-11 04:40:17 +00:00
Tim Mocny
e21d2408bb IKO - 9 April 2020-04-11 04:40:17 +00:00
Michael Kamensky
aede20c610 Merge branch 'mirrodin' into 'master'
Mirrodin Besieged corrections

See merge request core-developers/forge!2684
2020-04-10 10:11:37 +00:00
Tim Mocny
205f3462b1 Mirrodin Besieged corrections 2020-04-10 10:11:37 +00:00
Michael Kamensky
742cfea656 Merge branch 'agitatorant' into 'master'
agitator_ant.txt

See merge request core-developers/forge!2679
2020-04-09 05:13:38 +00:00
Tim Mocny
7af1f7a452 agitator_ant.txt 2020-04-09 05:13:38 +00:00
Michael Kamensky
e649db6775 Merge branch 'iko8' into 'master'
IKO - 8 April

See merge request core-developers/forge!2680
2020-04-09 05:06:48 +00:00
Tim Mocny
461b72376f IKO - 8 April 2020-04-09 05:06:47 +00:00
Michael Kamensky
9b03426f0e Merge branch 'cache' into 'master'
Mana Cache!

Closes #81

See merge request core-developers/forge!2639
2020-04-09 05:06:11 +00:00
Tim Mocny
e1b14ecb51 Mana Cache! 2020-04-09 05:06:11 +00:00
Michael Kamensky
972327bd72 Merge branch 'iko7' into 'master'
IKO - 7 April

See merge request core-developers/forge!2676
2020-04-09 05:04:35 +00:00
Tim Mocny
db25466646 IKO - 7 April 2020-04-09 05:04:35 +00:00
Michael Kamensky
68ad5ad9c0 Merge branch 'typo' into 'master'
Random typo

See merge request core-developers/forge!2678
2020-04-09 05:04:12 +00:00
Tim Mocny
053fc12b35 Random typo 2020-04-09 05:04:12 +00:00
Michael Kamensky
5ec5cb5f7f Merge branch 'newBranch' into 'master'
fix desktop generated name

See merge request core-developers/forge!2681
2020-04-09 05:03:59 +00:00
Anthony Calosa
ccf2abc391 fix desktop generated name 2020-04-09 08:46:37 +08:00
CCTV-1
d2e5eb133b Merge remote-tracking branch 'upstream/master' 2020-04-08 08:47:11 +08:00
Michael Kamensky
5ba4ebbe38 Merge branch 'sld' into 'master'
SLD cards 59-62

Closes #1342

See merge request core-developers/forge!2677
2020-04-07 17:06:24 +00:00
Tim Mocny
5c1702c25b SLD cards 59-62 2020-04-07 17:06:24 +00:00
Michael Kamensky
65da3d55a2 Merge branch 'newBranch' into 'master'
Update Disconnect button view

See merge request core-developers/forge!2675
2020-04-07 17:06:18 +00:00
Anthony Calosa
3e1fe53b77 Update Disconnect button view 2020-04-07 19:00:53 +08:00
Michael Kamensky
0b9a9fc2a9 Merge branch 'iko6' into 'master'
IKO - 6 April

See merge request core-developers/forge!2671
2020-04-07 04:21:17 +00:00
Tim Mocny
86ff6b402a IKO - 6 April 2020-04-07 04:21:17 +00:00
Michael Kamensky
c43b14f850 Merge branch 'newBranch' into 'master'
Fix 180 rotation bug on Mobile

See merge request core-developers/forge!2670
2020-04-07 04:18:59 +00:00
Anthony Calosa
45b1e825e1 Fix 180 rotation bug on Mobile
(to repro, create local game human vs human, 2nd player will be rotated as usual, concede and
create a network game again, human vs human, or human vs AI)
2020-04-06 21:32:05 +08:00
CCTV-1
fa96bd5b5d fix grammar errors 2020-04-06 18:30:54 +08:00
CCTV-1
6c671eaf9d update translations 2020-04-06 18:24:03 +08:00
CCTV-1
190be3f7ea translate new option 2020-04-06 18:23:37 +08:00
CCTV-1
7642d588a4 Merge remote-tracking branch 'upstream/master' 2020-04-06 18:12:05 +08:00
Hans Mackowiak
a43b2a859b Merge branch 'revert-88736fb3' into 'master'
Keyword counter MK2

See merge request core-developers/forge!2669
2020-04-06 05:27:39 +00:00
Hans Mackowiak
5c79cb7de6 Revert "Merge branch 'revert-93f1bb70' into 'master'"
This reverts merge request !2668
2020-04-06 05:27:39 +00:00
Sol
b80d499a36 Merge branch 'startDuelSound' into 'master'
Start duel sound + correction in Unstable edition file (3 missing tokens)

See merge request core-developers/forge!2666
2020-04-06 02:48:25 +00:00
BlueWizard
0b5c70b4bd Start duel sound + correction in Unstable edition file (3 missing tokens) 2020-04-06 02:48:25 +00:00
Sol
cb4a4d81ae Merge branch 'iko5' into 'master'
IKO - 5 April

See merge request core-developers/forge!2664
2020-04-06 02:48:01 +00:00
Tim Mocny
349640a3a0 IKO - 5 April 2020-04-06 02:48:01 +00:00
Sol
c88399d7a0 Merge branch 'newBranch' into 'master'
Add network options, and some fixes during netplay,

See merge request core-developers/forge!2663
2020-04-06 02:45:15 +00:00
Anthony Calosa
aa86f8eae3 Forge constant for close command message 2020-04-06 09:21:10 +08:00
Anthony Calosa
b6408100e5 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-06 08:25:35 +08:00
Sol
88736fb3a7 Merge branch 'revert-93f1bb70' into 'master'
Revert "Merge branch 'keywordCounter' into 'master'"

See merge request core-developers/forge!2668
2020-04-06 00:11:34 +00:00
Sol
8523288884 Revert "Merge branch 'keywordCounter' into 'master'"
This reverts merge request !2667
2020-04-05 23:56:37 +00:00
Anthony Calosa
3d46a6e809 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-06 06:15:52 +08:00
Anthony Calosa
c82ed43c69 Update description 2020-04-06 05:18:53 +08:00
Anthony Calosa
9edaa9561e Add Disconnect for Mobile 2020-04-06 05:02:25 +08:00
Michael Kamensky
93f1bb70b4 Merge branch 'keywordCounter' into 'master'
Keyword counter

Closes #1338

See merge request core-developers/forge!2667
2020-04-05 20:13:43 +00:00
Hans Mackowiak
3bb42a95c9 CounterPutEffect: add Choices with inbuilt ChosenCard Effect 2020-04-05 21:57:32 +02:00
Hans Mackowiak
6ce62b7316 Keyword Counter: special counters add keywords to cards 2020-04-05 17:29:21 +02:00
Michael Kamensky
e40388c244 Merge branch 'C2020-3' into 'master'
C20 - Jirina & Kalamax

See merge request core-developers/forge!2659
2020-04-05 09:03:18 +00:00
Tim Mocny
8eab842416 C20 - Jirina & Kalamax 2020-04-05 09:03:18 +00:00
Michael Kamensky
4febc835e3 Merge branch 'grammar' into 'master'
Little grammar fix

See merge request core-developers/forge!2660
2020-04-05 09:02:27 +00:00
Tim Mocny
56dbb45d63 Little grammar fix 2020-04-05 09:02:26 +00:00
Michael Kamensky
3b50a9ef8b Merge branch 'iko4' into 'master'
IKO - 4 April

See merge request core-developers/forge!2661
2020-04-05 09:02:24 +00:00
Tim Mocny
ba3e8de481 IKO - 4 April 2020-04-05 09:02:24 +00:00
Anthony Calosa
a18f142add Update SettingsPage Desktop 2020-04-05 12:34:56 +08:00
Anthony Calosa
c2e4888711 Merge remote-tracking branch 'origin/newBranch' into newBranch 2020-04-05 11:43:49 +08:00
Anthony Calosa
b466ff38a3 Update SettingsPage 2020-04-05 11:41:21 +08:00
Anthony Calosa
96ce695aae Remove unnecessary lines 2020-04-05 01:15:09 +00:00
Anthony Calosa
52307c02d0 Add option to turn on Experimental Network Compatibility
the user can try to turn on this option to test compatibility, defaults to OFF
2020-04-05 07:14:03 +08:00
Anthony Calosa
c714187eee Refactor Object Decoder/Encoder
Prevent crash on netplay
2020-04-05 07:07:30 +08:00
Anthony Calosa
9f1b93373d Refactor check permission without supportv4 dependency 2020-04-05 07:02:13 +08:00
Michael Kamensky
eeacdf6c80 Merge branch 'fixCyclingZero' into 'master'
GameActionUtil: fix CyclingForZero

Closes #1341

See merge request core-developers/forge!2662
2020-04-04 14:17:47 +00:00
Hans Mackowiak
bf60039ee7 GameActionUtil: fix CyclingForZero 2020-04-04 14:17:47 +00:00
CCTV-1
78b2972943 Merge remote-tracking branch 'upstream/master' 2020-04-04 19:48:28 +08:00
Michael Kamensky
3eb322222a Merge branch 'lukka' into 'master'
lukka_coppercoat_outcast.txt

Closes #1340

See merge request core-developers/forge!2655
2020-04-04 11:00:41 +00:00
Tim Mocny
ef95d3eab9 lukka_coppercoat_outcast.txt 2020-04-04 11:00:41 +00:00
Michael Kamensky
829e49a2ba Merge branch 'patch-9' into 'master'
Mobile: Remove duplicated Enable Unknown Cards in SettingsPage.java

See merge request core-developers/forge!2657
2020-04-04 10:59:54 +00:00
Michael Kamensky
0a768211b9 Merge branch 'MTGA_Cube' into 'master'
support mtga cube

See merge request core-developers/forge!2658
2020-04-04 10:59:23 +00:00
CCTV-1
d0bdeec832 support mtga cube 2020-04-04 18:03:12 +08:00
klaxnek
9047168a3f Remove duplicated Enable Unknown Cards in SettingsPage.java 2020-04-04 09:14:47 +00:00
CCTV-1
9a2f2368e9 Merge remote-tracking branch 'upstream/master' 2020-04-04 16:47:07 +08:00
Michael Kamensky
5cf56e12c3 Merge branch 'iko2' into 'master'
Ikoria round 1

See merge request core-developers/forge!2654
2020-04-04 03:52:01 +00:00
Tim Mocny
a67deeda15 Ikoria round 1 2020-04-04 03:52:00 +00:00
Michael Kamensky
0b2a1ce44a Merge branch 'zilortha' into 'master'
Zilortha, Strength Incarnate

Closes #1337

See merge request core-developers/forge!2653
2020-04-04 03:51:32 +00:00
Tim Mocny
5ab7bcaf3f Zilortha, Strength Incarnate 2020-04-04 03:51:32 +00:00
Michael Kamensky
18bd6ec204 Merge branch 'newBranch' into 'master'
Add simple instructions for permission and Netty update

See merge request core-developers/forge!2652
2020-04-04 03:51:01 +00:00
Anthony Calosa
da984227b1 Use Netty Default Object Decoder/Encoder 2020-04-03 21:35:05 +08:00
Anthony Calosa
bd5cc06504 remove unused imports 2020-04-03 19:02:48 +08:00
Anthony Calosa
8f99034a02 Fix crash 2020-04-03 19:01:03 +08:00
Anthony Calosa
d66700cbf3 Preference for Desktop 2020-04-03 18:47:33 +08:00
Anthony Calosa
bb62c0bebb Preference 2020-04-03 18:00:44 +08:00
Anthony Calosa
13652e30ac Add Elsa Serializer Option (Off by default) 2020-04-03 18:00:10 +08:00
Anthony Calosa
b7d93783c0 Update Netty to 4.1.48.Final 2020-04-03 17:56:57 +08:00
Anthony Calosa
5ba9f5f58f Move Option 2020-04-03 16:36:13 +08:00
Anthony Calosa
fa93702bc8 fix android - pc network (disables elsa atm, needs investigation since this work before) 2020-04-03 12:53:43 +08:00
Anthony Calosa
c9c4d5492d Use statelist and onclick listener so the buttons don't bug out 2020-04-03 10:58:03 +08:00
Anthony Calosa
bcec151b1d button color pressed state 2020-04-03 09:43:25 +08:00
Anthony Calosa
4039c28140 Remade the instructions (much better way) 2020-04-03 09:04:55 +08:00
Anthony Calosa
a5ad3ad60a Add faster way to access storage permission (app details) 2020-04-03 00:50:23 +08:00
CCTV-1
d3e83b2601 translate new option 2020-04-02 21:35:12 +08:00
CCTV-1
0aee987536 Merge remote-tracking branch 'upstream/master' 2020-04-02 21:32:41 +08:00
Michael Kamensky
495257882b Merge branch 'newBranch' into 'master'
Update LibGDX March 29, 2020, Use Google cache

See merge request core-developers/forge!2649
2020-04-02 13:17:58 +00:00
Anthony Calosa
58f6463a5d Add reminder text on Storage Permission
The app displays message when it doesn't start properly because of Storage Permission
2020-04-02 18:20:01 +08:00
Michael Kamensky
65b7f2f4a3 Merge branch 'master' into 'master'
Added puzzles PS_THB8 and PS_THB9 - Possibility Storm - Theros Beyond Death 08 and 09.

See merge request core-developers/forge!2651
2020-04-02 09:30:49 +00:00
Agetian
ed67aafe2e - Added puzzles PS_THB8 and PS_THB9. 2020-04-02 12:25:29 +03:00
Anthony Calosa
71cc16953c Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-04-02 12:24:28 +08:00
Anthony Calosa
9a1e467bb7 Update LibGDX March 29, 2020, Use Google cache
(reverted the cache so there's room for dependency like android-support-v4, permissions in the future,
lighter than cache2k)
2020-04-02 12:15:01 +08:00
Michael Kamensky
a4d9e1ef84 Merge branch 'silverbl' into 'master'
Silverblade Paladin detail cleanup

See merge request core-developers/forge!2648
2020-03-31 17:15:35 +00:00
Tim Mocny
41dbc468bd Silverblade Paladin detail cleanup 2020-03-31 17:15:35 +00:00
Michael Kamensky
8a9984f7df Merge branch 'newBranch' into 'master'
Fix Plane and Phenomenon not triggering when the player PlaneswalkTo/Encounter to it...

See merge request core-developers/forge!2646
2020-03-31 15:47:56 +00:00
Anthony Calosa
eca7ea2fbe Fix Skybreen 2020-03-31 23:41:20 +08:00
Anthony Calosa
05e160cebe Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-31 23:15:33 +08:00
Anthony Calosa
447c3dff5a Fix Plane and Phenomenon not triggering when the player PlaneswalkTo/Encounter to it... 2020-03-31 23:12:22 +08:00
Michael Kamensky
2dd97028b1 Merge branch 'tawnos' into 'master'
Tawnos's Coffin!

Closes #114

See merge request core-developers/forge!2637
2020-03-31 13:11:46 +00:00
Tim Mocny
24db729e96 Tawnos's Coffin! 2020-03-31 13:11:46 +00:00
Michael Kamensky
c10c9f441f Merge branch 'newBranch' into 'master'
Fix stack description

See merge request core-developers/forge!2645
2020-03-31 07:51:33 +00:00
Anthony Calosa
6bf72e6828 space 2020-03-31 14:49:24 +08:00
Anthony Calosa
bf7b446586 period 2020-03-31 14:46:42 +08:00
Anthony Calosa
ec463b414f Refactor description 2020-03-31 14:44:37 +08:00
Anthony Calosa
faf8c57907 Fix stack description
Fix "null damage" & "null counter" stack description, and Volcano Hellion crash
2020-03-31 13:36:45 +08:00
Michael Kamensky
7b90b6a801 Merge branch 'faerieart' into 'master'
Faerie Artisans - trigger tidying

See merge request core-developers/forge!2642
2020-03-31 05:03:55 +00:00
Tim Mocny
cd8410e87a Faerie Artisans - trigger tidying 2020-03-31 05:03:55 +00:00
Michael Kamensky
7dbf6519a0 Merge branch 'nissapilgfix' into 'master'
Nissa's Pilgrimage fix

See merge request core-developers/forge!2641
2020-03-31 05:03:35 +00:00
Tim Mocny
9811eedfc5 Nissa's Pilgrimage fix 2020-03-31 05:03:34 +00:00
Michael Kamensky
e337107575 Merge branch 'narcomofix' into 'master'
narcomoeba.txt fix

See merge request core-developers/forge!2640
2020-03-31 05:02:47 +00:00
Tim Mocny
70b7af6f16 narcomoeba.txt fix 2020-03-31 05:02:47 +00:00
Michael Kamensky
17584a03f1 Merge branch 'newBranch' into 'master'
Add preference to enable/disable loading of Unknown Cards.

See merge request core-developers/forge!2644
2020-03-31 05:01:51 +00:00
Michael Kamensky
2472db72ca Merge branch 'fix-multiplayer-crash' into 'master'
A player who lost cannot initiate combat

Closes #1255

See merge request core-developers/forge!2643
2020-03-31 05:01:24 +00:00
Anthony Calosa
e57e87d3c2 Fix double "- - " on stack description 2020-03-31 12:01:57 +08:00
Anthony Calosa
7c9849f845 Add preference to enable/disable loading of Unknown Cards.
If a user don't wants to load a certain cards from deleted set/s in edition folder,
you can do so by disabling this option.
2020-03-31 10:54:27 +08:00
friarsol
1c61030aae A player who lost cannot initiate combat 2020-03-30 21:40:46 -04:00
Sol
bd8b136b2b Merge branch 'pyramids' into 'master'
Pyramids!

Closes #95

See merge request core-developers/forge!2635
2020-03-29 02:39:47 +00:00
Tim Mocny
4bb0bcfa90 Pyramids! 2020-03-29 02:39:46 +00:00
Sol
084a9812a7 Merge branch 'draftable' into 'master'
More CN2 "draft matters" draftable

See merge request core-developers/forge!2632
2020-03-29 02:38:49 +00:00
Tim Mocny
65947bee28 More CN2 "draft matters" draftable 2020-03-29 02:38:48 +00:00
Sol
10acab8bb9 Merge branch 'naturalunity' into 'master'
Natural Unity fix

See merge request core-developers/forge!2634
2020-03-29 02:38:16 +00:00
Tim Mocny
eb03c01cb0 Natural Unity fix 2020-03-29 02:38:16 +00:00
Sol
9f8f7f132a Merge branch 'perimeter' into 'master'
Hold the Perimeter!

Closes #67

See merge request core-developers/forge!2631
2020-03-29 02:37:37 +00:00
Tim Mocny
62aec9956e Hold the Perimeter! 2020-03-29 02:37:36 +00:00
Michael Kamensky
9376ba3429 Merge branch 'mb1tweak' into 'master'
MB1 printsheets tweak

See merge request core-developers/forge!2630
2020-03-27 09:34:28 +00:00
Tim Mocny
7f8ddb0cab MB1 printsheets tweak 2020-03-27 09:34:28 +00:00
Michael Kamensky
dd3c93287e Merge branch 'latestbling' into 'master'
Editions files for SLU and SS3

See merge request core-developers/forge!2624
2020-03-26 04:19:05 +00:00
Tim Mocny
6da9d86b1b Editions files for SLU and SS3 2020-03-26 04:19:04 +00:00
Michael Kamensky
ad4d429aff Merge branch 'typo' into 'master'
SLD edition typo - rogue comma

See merge request core-developers/forge!2629
2020-03-26 04:18:26 +00:00
Tim Mocny
051e607724 SLD edition typo - rogue comma 2020-03-26 04:18:25 +00:00
Michael Kamensky
cc1f5abe28 Merge branch 'master' into 'master'
- Preparing Forge for Android publish v1.6.33.001 [incremental]

See merge request core-developers/forge!2628
2020-03-22 08:09:47 +00:00
Agetian
27a6f9c3a4 - Preparing Forge for Android publish v1.6.33.001 [incremental] 2020-03-22 11:02:04 +03:00
Michael Kamensky
37eeec088d Merge branch 'emershep' into 'master'
Tidying BFZ rare Landfall creature cycle

See merge request core-developers/forge!2627
2020-03-22 04:06:20 +00:00
Tim Mocny
1c3e8c32b9 Tidying BFZ rare Landfall creature cycle 2020-03-22 04:06:19 +00:00
Sol
ee1b8d1c9a Merge branch 'historic' into 'master'
MTGA Historic Format

See merge request core-developers/forge!2622
2020-03-20 19:11:59 +00:00
Tim Mocny
7fb34b4887 MTGA Historic Format 2020-03-20 19:11:59 +00:00
Michael Kamensky
b65b0e4758 Merge branch 'fix-edition-tracking' into 'master'
Update EditionTracking for new Edition file things

See merge request core-developers/forge!2626
2020-03-20 11:13:36 +00:00
friarsol
4e8e77924a Update EditionTracking for new Edition file things 2020-03-19 23:24:48 -04:00
CCTV-1
3e7f8c8916 update translation 2020-03-20 11:13:50 +08:00
CCTV-1
5005cf9102 Merge remote-tracking branch 'upstream/master' 2020-03-20 11:07:17 +08:00
Blacksmith
dafd2e6a2a Clear out release files in preparation for next release 2020-03-20 00:42:01 +00:00
Blacksmith
4cfcbaf603 [maven-release-plugin] prepare for next development iteration 2020-03-20 00:35:58 +00:00
Blacksmith
22dc1a27a0 [maven-release-plugin] prepare release forge-1.6.33 2020-03-20 00:35:52 +00:00
Blacksmith
c374e0ae81 Update README.txt for release 2020-03-20 00:32:15 +00:00
Michael Kamensky
fc1b3547b2 Merge branch 'patch-5' into 'master'
Update DebuffEffect: fix if it doesn't use target

Closes #1325

See merge request core-developers/forge!2625
2020-03-19 06:38:22 +00:00
Hans Mackowiak
b0d3fbe783 Update DebuffEffect: fix if it doesn't use target 2020-03-19 06:38:22 +00:00
Michael Kamensky
3a02a10258 Merge branch 'voteKeywords' into 'master'
Vote: replace hidden keywords with timestamped properties

Closes #57, #1318, and #1258

See merge request core-developers/forge!2510
2020-03-19 06:22:44 +00:00
Hans Mackowiak
33d83dc0c5 Vote: replace hidden keywords with timestamped properties 2020-03-19 06:22:44 +00:00
Sol
d35a2cdead Merge branch 'peltcol' into 'master'
Pelt Collector fixup

See merge request core-developers/forge!2623
2020-03-17 12:40:38 +00:00
Tim Mocny
507c88269b Pelt Collector fixup 2020-03-17 12:40:37 +00:00
Michael Kamensky
2c7dfc0c41 Merge branch 'glyphre' into 'master'
Glyph of Reincarnation fixup

See merge request core-developers/forge!2621
2020-03-16 20:00:15 +00:00
Tim Mocny
a8ab95f321 - closer to "from the graveyard of the player who controlled that creature the last time it became blocked by that Wall" 2020-03-16 20:00:14 +00:00
Michael Kamensky
672cff54ef Merge branch 'krovam2' into 'master'
Krovikan Vampire - a bit more Cleanup

See merge request core-developers/forge!2620
2020-03-16 19:59:34 +00:00
Tim Mocny
44cbd9af3e Krovikan Vampire - a bit more Cleanup 2020-03-16 19:59:34 +00:00
Michael Kamensky
d3bcfea727 Merge branch 'krovam' into 'master'
Krovikan Vampire!

Closes #75

See merge request core-developers/forge!2619
2020-03-15 16:19:09 +00:00
Tim Mocny
20fa72d24e Krovikan Vampire! 2020-03-15 16:19:09 +00:00
Michael Kamensky
10faa20490 Merge branch 'newBranch' into 'master'
Stack display adjustment

See merge request core-developers/forge!2612
2020-03-15 06:06:38 +00:00
Sol
62c9d187eb Merge branch 'respectbanlist' into 'master'
Respect ban list in archetype deck generation and updated deckgen data

See merge request core-developers/forge!2618
2020-03-15 01:12:03 +00:00
austinio7116
a5148e8983 Added draft rankings 2020-03-14 21:19:19 +00:00
maustin
0bb7a84e55 Merge branch 'coremaster' into respectbanlist 2020-03-14 21:18:16 +00:00
Michael Kamensky
1218786b3e Merge branch 'master' into 'master'
A basic AI logic hook for Timmerian Fiends

See merge request core-developers/forge!2617
2020-03-14 16:45:51 +00:00
Agetian
62a1ee6d3d - A basic AI logic hook for Timmerian Fiends 2020-03-14 19:23:37 +03:00
Michael Kamensky
64f17b49fe Merge branch 'timmerian' into 'master'
Timmerian Fiends!

Closes #116

See merge request core-developers/forge!2610
2020-03-14 15:48:57 +00:00
Tim Mocny
0d939da60d Timmerian Fiends! 2020-03-14 15:48:56 +00:00
Michael Kamensky
cc321b1b2e Merge branch 'fixes' into 'master'
Fixes

Closes #1291, #1317, and #1322

See merge request core-developers/forge!2615
2020-03-14 15:42:36 +00:00
Michael Kamensky
71bf43b63a Merge branch 'glyphre' into 'master'
Glyph of Reincarnation!

Closes #64

See merge request core-developers/forge!2616
2020-03-14 15:41:43 +00:00
Tim Mocny
2d7840aa7d Glyph of Reincarnation! 2020-03-14 15:41:43 +00:00
Anthony Calosa
a1be9d0278 Fix Inventory View for Cards without Card Image 2020-03-14 13:34:31 +08:00
CCTV-1
1a99afa99e deduplication 2020-03-14 12:27:58 +08:00
CCTV-1
27eda6f9b2 Merge remote-tracking branch 'upstream/master' 2020-03-14 12:18:50 +08:00
Anthony Calosa
8a18f815b4 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-14 10:42:07 +08:00
Sol
2b529a07c2 Merge branch '1323-numberformatexception-for-input-string-2secondary-true' into 'master'
Resolve "NumberFormatException: For input string: "2Secondary$ True""

Closes #1323

See merge request core-developers/forge!2613
2020-03-14 00:55:55 +00:00
Jamin W. Collins
c0349d87fb implement and use new PredicateCard
This fixes the THB and ELD draft, possibly others

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-13 18:45:36 -06:00
Jamin W. Collins
c488ab4772 increase maximal heap size to 4096m
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-13 18:43:06 -06:00
Jamin W. Collins
d83f8deb7a remove vestigial scripts
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-13 18:43:06 -06:00
Jamin W. Collins
f1f480d16a explicitly set the encoding to UTF-8
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-13 18:43:06 -06:00
Hans Mackowiak
a1ffa625e1 CardFactoryUtil: fix Saga 2020-03-13 20:34:34 +00:00
Anthony Calosa
d7e38f7fd8 Format Stack Text
(Should be cleaner)
2020-03-14 03:05:29 +08:00
Anthony Calosa
a5683d4f12 Adjust Card Name & Ability Icons Ordering 2020-03-14 03:02:15 +08:00
Sol
cb1d4b7904 Merge branch 'updatertranslation' into 'master'
Auto Updater: Add missing translation strings

See merge request core-developers/forge!2598
2020-03-13 15:50:09 +00:00
Michael Kamensky
c6def2cc5b Merge branch 'Hanmac-Saga-Trigger' into 'master'
Sage Trigger Combined

See merge request core-developers/forge!2609
2020-03-13 15:11:05 +00:00
Hans Mackowiak
81dc9fecba Sage Trigger Combined 2020-03-13 15:11:05 +00:00
Sol
4629817268 Merge branch '12Mar20' into 'master'
the_triumph_of_anax.txt typo

See merge request core-developers/forge!2607
2020-03-12 22:11:37 +00:00
Tim Mocny
b6bbe3d18b the_triumph_of_anax.txt typo 2020-03-12 22:11:37 +00:00
Hans Mackowiak
b437db8ffa Merge branch 'tinyleadersPWcomm' into 'master'
Make Planeswalkers possible for Tiny Leaders

Closes #1311

See merge request core-developers/forge!2606
2020-03-12 16:00:04 +00:00
Tim Mocny
3be58fee75 Make Planeswalkers poss commander for Tiny Leaders, cleanup Brawl commander syntax
Legendary Planeswalkers only

Clean up Commander syntax for TL and Brawl
2020-03-12 16:00:03 +00:00
Michael Kamensky
fe09671795 Merge branch 'etbCounterManaPaid' into 'master'
ManaPart: use Effect for enter the battlefield when mana was spent

See merge request core-developers/forge!2604
2020-03-12 13:34:27 +00:00
Hans Mackowiak
9f35da4698 ManaPart: use Effect for enter the battlefield when mana was spent 2020-03-12 13:34:27 +00:00
Michael Kamensky
3c5a62056b Merge branch 'edfix9mar' into 'master'
Various fixes

See merge request core-developers/forge!2599
2020-03-12 05:40:05 +00:00
Tim Mocny
43a38e3c6d Various fixes 2020-03-12 05:40:05 +00:00
Michael Kamensky
f5a89afa93 Merge branch 'newBranch' into 'master'
remade border to have thin line edges

See merge request core-developers/forge!2603
2020-03-12 05:38:32 +00:00
Anthony Calosa
7a49dd28e1 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-12 12:16:20 +08:00
Sol
d14f20bc43 Merge branch 'collector_number' into 'master'
handle more complex imageKey parsing

See merge request core-developers/forge!2602
2020-03-12 02:56:33 +00:00
Anthony Calosa
14a40e493f Try first variant art for .fullborder images
(ie WAR variant has alternate art)
2020-03-12 08:54:08 +08:00
Anthony Calosa
9b8e63501a Reduce whiteborder sides 2020-03-11 22:21:48 +08:00
Anthony Calosa
61097e9f80 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-11 22:13:42 +08:00
Sol
404ce34076 Merge branch 'mar9banres' into 'master'
Brawl, Legacy, Modern bans

See merge request core-developers/forge!2605
2020-03-11 13:08:44 +00:00
Northmoc
e546b6a689 Brawl, Legacy, Modern bans 2020-03-11 09:01:11 -04:00
Anthony Calosa
7e31e15ead Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-11 16:52:34 +08:00
Anthony Calosa
62d7824155 remade border to have thin line edges
(simulate card edges)
2020-03-11 14:52:21 +08:00
Jamin W. Collins
8c5a90d6e9 handle more complex imageKey parsing
Should now be able to handle:
c:Town Gossipmonger|SOI|1$alt

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-10 21:23:29 -06:00
Sol
9345ff6725 Merge branch 'cardfixes' into 'master'
Card Fixes: Settle the Wreckage, Cavalier of Dawn

See merge request core-developers/forge!2601
2020-03-11 01:25:13 +00:00
Michael Kamensky
c45573ff2e Merge branch 'newBranch' into 'master'
Minor Visual Improvement & Update Android target SDK

See merge request core-developers/forge!2600
2020-03-10 18:10:14 +00:00
Adam Pantel
55300347ce Fix Settle the Wreckage, Cavalier of Dawn 2020-03-10 12:46:51 -04:00
Anthony Calosa
c85584445e update project.properties 2020-03-10 17:16:08 +08:00
Anthony Calosa
ceeb5d623f Increase Android target SDK so it will work with Android 10.
Need to download SDK 26 to compile.

(Just tested this on Android 6 and Android 9, it works, but need to test the change for Android 5 and Android 10, for Android 10, It needs to turn the storage permission settings manually since the project don't have running permission setup currently)
2020-03-10 16:53:59 +08:00
Anthony Calosa
1f4eff8b1c Adjust CroppedArea for Non Modern Card Frames
(fullborder images)
2020-03-10 16:45:08 +08:00
Anthony Calosa
41af869d3c Update Font 2020-03-10 16:35:39 +08:00
Michael Kamensky
2747d93e4c Merge branch 'fixXonCancel' into 'master'
GameAction: refactor setting CastSA

Closes #1320

See merge request core-developers/forge!2585
2020-03-10 06:35:34 +00:00
Hans Mackowiak
5c9251e295 SpellAbility: adding an Announce needs to be to originalMapParams too 2020-03-10 07:13:04 +01:00
Hans Mackowiak
fa67ee73a5 Card: cleanup multikicker values 2020-03-10 07:13:04 +01:00
Hans Mackowiak
e1659a4539 Combat: use lkiCase only if blocker itself is an lki 2020-03-10 07:13:04 +01:00
Hans Mackowiak
d5b578b306 Card: move reseting xmana paid to clearTemporaryVars 2020-03-10 07:13:04 +01:00
Hans Mackowiak
32b56018a6 GameAction: refactor setting CastSA 2020-03-10 07:13:04 +01:00
Peter
aa12085345 Add new translation strings about the new Auto Updater 2020-03-09 19:54:11 +01:00
Michael Kamensky
3dd20d9d0b Merge branch 'master' into 'master'
Fix AddsCounters

Closes #1264

See merge request core-developers/forge!2597
2020-03-09 18:27:54 +00:00
Agetian
8b922fc8f2 - Fix AddsCounters 2020-03-09 21:13:29 +03:00
CCTV-1
a527211bcf translate AutoUpdater 2020-03-09 20:15:36 +08:00
CCTV-1
c769a8adcf Merge remote-tracking branch 'upstream/master' 2020-03-09 19:21:43 +08:00
Agetian
c88a419e03 - Use ExecuteScript in PS_THB7 for a simpler implementation. 2020-03-09 12:32:06 +03:00
Michael Kamensky
b9618509be Merge branch 'desktop-autoupdate' into 'master'
check for updates button

See merge request core-developers/forge!2537
2020-03-09 03:59:40 +00:00
Sol
1076b1e29f Merge branch 'edition-fixes' into 'master'
improve handling of foil-only Kaya for CN2

See merge request core-developers/forge!2584
2020-03-09 01:10:26 +00:00
Jamin W. Collins
4717955afb improve handling of foil-only Kaya for CN2
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-08 09:43:58 -06:00
Michael Kamensky
af771d0c2c Merge branch 'master' into 'master'
Added puzzles PS_THB6 and PS_THB7 - Possibility Storm - Theros Beyond Death 06 and 07

See merge request core-developers/forge!2595
2020-03-08 12:12:36 +00:00
Michael Kamensky
8fea6b4cc0 Merge branch 'newBranch' into 'master'
Add Cancel Button for Puzzle Screen

See merge request core-developers/forge!2596
2020-03-08 12:12:30 +00:00
Anthony Calosa
9249029dc1 Add Cancel Button for Puzzle Screen
(If you decide not to solve puzzle or accidentally hit start on Puzzle Menu)
2020-03-08 18:37:36 +08:00
Agetian
e5d40554e1 - Error prevention on ID-based precast 2020-03-08 13:16:10 +03:00
Agetian
8b8e39ff41 - Added puzzles PS_THB6 and PS_THB7.
- Added a way to precast a spell from a specific host by ID.
2020-03-08 13:12:28 +03:00
Michael Kamensky
40591b04d2 Merge branch 'newBranch' into 'master'
Update Menu & App Icons

See merge request core-developers/forge!2591
2020-03-08 09:10:44 +00:00
Michael Kamensky
1c1e2416e4 Merge branch 'patch-4' into 'master'
Replace bg_match.jpg in Darkred skin

See merge request core-developers/forge!2593
2020-03-08 09:10:06 +00:00
CCTV-1
684e9327cd update forge-android support simplified chinese characters. 2020-03-08 16:22:20 +08:00
CCTV-1
5906891fec update translation 2020-03-08 16:18:02 +08:00
CCTV-1
e0c9d5e6e0 Merge remote-tracking branch 'upstream/master' 2020-03-08 16:11:00 +08:00
Anthony Calosa
bdf9d3f88e Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-08 12:34:47 +08:00
Sol
960760a564 Merge branch 'mb1fix' into 'master'
MB1 pool corrections

See merge request core-developers/forge!2590
2020-03-08 02:13:25 +00:00
Tim Mocny
f488cb1d0b MB1 pool corrections 2020-03-08 02:13:25 +00:00
Sol
c4e2004af3 Merge branch 'fixes' into 'master'
simplify regex usage to group numbers, support Android 6

See merge request core-developers/forge!2592
2020-03-08 02:11:07 +00:00
friarsol
dcb151f561 Get the Updater liike 90% of the way there. 2020-03-07 20:37:07 -05:00
Churrufli
4550ee26e3 Replace bg_match.jpg, previous one is a png renamed, I noticed may cause bugs in some Android versions 2020-03-07 18:15:23 +00:00
Jamin W. Collins
ecdea545fc simplify regex usage to group numbers, support Android 6
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-07 09:01:57 -07:00
Anthony Calosa
643c08850b Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-07 13:21:38 +08:00
Anthony Calosa
fed6d788cf Update Menu Icons 2020-03-07 13:21:13 +08:00
Anthony Calosa
3ccd979b63 Update App Icon 2020-03-07 13:20:07 +08:00
Michael Kamensky
be52fb772e Merge branch 'onceupon' into 'master'
Once Upon a Time (lowercase "a" throughout Forge)

See merge request core-developers/forge!2589
2020-03-06 15:30:37 +00:00
Tim Mocny
633c106708 Once Upon a Time (lowercase "a" throughout Forge) 2020-03-06 15:30:36 +00:00
Michael Kamensky
aec62cbffb Merge branch 'mysteryboost' into 'master'
Mystery Booster support!

See merge request core-developers/forge!2588
2020-03-06 15:30:25 +00:00
Tim Mocny
5b61712d70 Mystery Booster support! 2020-03-06 15:30:25 +00:00
Michael Kamensky
7fa248db4c Merge branch 'patch-joust' into 'master'
Update joust: add missing AILogic$ Fight

See merge request core-developers/forge!2586
2020-03-05 14:32:18 +00:00
Hans Mackowiak
33f04148d3 Update joust: add missing AILogic$ Fight 2020-03-05 14:32:17 +00:00
Michael Kamensky
3aa33be214 Merge branch 'patch-collector-number-thb' into 'master'
Update Theros Beyond Death: add missing CollectorNumber

See merge request core-developers/forge!2587
2020-03-05 14:32:11 +00:00
Hans Mackowiak
612045f8c0 Update Theros Beyond Death: add missing CollectorNumber 2020-03-05 14:32:11 +00:00
Michael Kamensky
0bd6b16247 Merge branch 'edition-fixes' into 'master'
Edition fixes

See merge request core-developers/forge!2583
2020-03-05 03:35:42 +00:00
Michael Kamensky
eaa7d296d2 Merge branch 'newBranch' into 'master'
Fix jerky animation on Conquest Reward Dialog

See merge request core-developers/forge!2581
2020-03-05 03:32:26 +00:00
Jamin W. Collins
235162fb84 fixing the collector numbers for Magic 2015
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 20:06:09 -07:00
Jamin W. Collins
fc4a491111 fixing collector numbers for Magic 2014
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 20:04:36 -07:00
Jamin W. Collins
0cd9b1435b fixing collector numbers for Khans of Tarkir
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 20:02:16 -07:00
Sol
61184ade4c Merge branch 'collector_number' into 'master'
change collector number to be a String

See merge request core-developers/forge!2578
2020-03-05 02:55:36 +00:00
Jamin W. Collins
f2e29c9c93 fixing collector numbers for Fate Reforged
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:54:54 -07:00
Sol
e676073548 Merge branch 'network-play-fixes' into 'master'
Network play fixes

See merge request core-developers/forge!2582
2020-03-05 02:51:16 +00:00
Jamin W. Collins
6b315b2571 adding missing card for Conspiracy Take the Crown
Kaya, Ghost Assassin is a foil-only booster card

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:46:40 -07:00
Jamin W. Collins
6e028a1c45 correct the rarities for Beatdown
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:40:27 -07:00
Jamin W. Collins
f07e2bc2ec correct the rarities for Battlebond
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:40:04 -07:00
Jamin W. Collins
3bc4124fe8 correct the rarities for Battle Royale
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:37:55 -07:00
Jamin W. Collins
bb5af6298e correct the collect numbers for Aether Revolt
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:32:28 -07:00
Jamin W. Collins
3c6c0f534d correct the tokens delimeter for Weatherlight
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:29:44 -07:00
Jamin W. Collins
4f3a8590dd semi-ignore non-fatal IndexOutOfBoundsException
Not entirely sure why there are IndexOutOfBoundsException being seen
currently, but they are not fatal and do not appear to result in game
state skew between client and server.  So, logging and moving on.

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:08:02 -07:00
Jamin W. Collins
2a993eb0e0 fixing openZones method signature for network-play
Changes introduced in ef0a1a84 altered the method signature for
openZones, but did not fully update the signature for network-play
calls.

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-04 19:06:07 -07:00
Anthony Calosa
f99e819ff0 Add puzzle improvements to Mobile Forge 2020-03-05 05:45:11 +08:00
Anthony Calosa
0f6b65a9ff Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-04 19:40:09 +08:00
austinio7116
6e94378cc1 Added filter to ensure banlist is respected 2020-03-04 06:05:07 +00:00
Jamin W. Collins
ff31718839 correct how collectorNumber is located
The existing logic will always return the first match for a given card
name, even if there are multiple different printings of the same card
name within a set. This change aligns the collectorNumber with the
alternate art index.

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-03 21:27:05 -07:00
Michael Kamensky
2ab5a286c5 Merge branch '030320' into 'master'
March 3 fixes

See merge request core-developers/forge!2580
2020-03-04 04:07:14 +00:00
Northmoc
e07208e81b dissenters_deliverance.txt correct filename 2020-03-03 14:15:54 -05:00
Hans Mackowiak
1ec9c6cade Merge branch 'glyphofdelusion' into 'master'
Glyph of Delusion

Closes #63

See merge request core-developers/forge!2482
2020-03-03 16:24:42 +00:00
Tim Mocny
04e94f68f5 Glyph of Delusion 2020-03-03 16:24:41 +00:00
Jamin W. Collins
e325f42ca8 change collector number to be a String
This change moves the internal storage of collector number from Integer
to String.

The parsed pattern for valid collector numbers is any number of digits
followed by an optional single non-digit character. This follows the
format used by Scryfall for the alternate art WAR planeswalkers and
cards from older formats sucha as FEM.

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-03 08:59:09 -07:00
Michael Kamensky
f3c078b23c Merge branch 'Hanmac-master-patch-40459' into 'master'
CardProperty: extend blockedValidThisTurn and blockedByValidThisTurn using defined

Closes #1299

See merge request core-developers/forge!2579
2020-03-03 14:30:40 +00:00
Hans Mackowiak
4f0deea15f CardProperty: extend blockedValidThisTurn and blockedByValidThisTurn using defined 2020-03-03 14:30:39 +00:00
Michael Kamensky
db396e6b5c Merge branch '020320' into 'master'
Fixes - 2 Mar 2020

See merge request core-developers/forge!2577
2020-03-03 04:07:39 +00:00
Tim Mocny
dfe7ea0ecc Fixes - 2 Mar 2020 2020-03-03 04:07:39 +00:00
Michael Kamensky
55cba2513f Merge branch 'witchHuntAi' into 'master'
ControlGainAi: fix target logic for Witch Hunt

See merge request core-developers/forge!2576
2020-03-02 06:08:02 +00:00
Hans Mackowiak
9bbc3c8ef2 ControlGainAi: fix target logic for Witch Hunt 2020-03-02 06:59:33 +01:00
Anthony Calosa
c473ecb827 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-02 12:30:51 +08:00
Anthony Calosa
f654982e38 Fix Card Reveal Animation for default display 2020-03-02 12:30:02 +08:00
Michael Kamensky
38394ee263 Merge branch 'fixes' into 'master'
fix message when no cards in opponent's hand

See merge request core-developers/forge!2575
2020-03-02 03:55:12 +00:00
Jamin W. Collins
c9010d4224 fix message when no cards in opponent's hand
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-01 14:04:08 -07:00
Michael Kamensky
cb1b8024b4 Merge branch '1303-control-change-and-cda-not-reset' into 'master'
Resolve "Control Change and CDA not reset?"

Closes #1303

See merge request core-developers/forge!2573
2020-03-01 17:15:39 +00:00
Hans Mackowiak
075a0e99af Merieke Ri Berit: use Effect for Destroy Trigger 2020-03-01 17:37:06 +01:00
Jamin Collins
5f4c86697a Merge branch 'sld37' into 'master'
SLD - Thalia

See merge request core-developers/forge!2574
2020-03-01 15:42:59 +00:00
Tim Mocny
1fee06c843 SLD - Thalia 2020-03-01 15:42:59 +00:00
Hans Mackowiak
23bc7a629b Fix controllerChangeZoneCorrection when ControlGain is undone 2020-03-01 11:47:51 +01:00
Hans Mackowiak
86ec94ca0d Merge branch '1300-crash-on-desktop-1-6-32-snapshot-r123-2020-01-27-1-pm' into 'master'
Resolve "Crash on Desktop 1.6.32 Snapshot r123 (2020-01-27 ~1 PM)"

Closes #1300

See merge request core-developers/forge!2555
2020-03-01 10:30:45 +00:00
Hans Mackowiak
93f0ade107 MustBlockEffect: use Choose Card for Crashing Boars 2020-03-01 10:30:45 +00:00
Michael Kamensky
40bb3d2750 Merge branch 'newBranch' into 'master'
Update Sprite Icons (Default Theme)

See merge request core-developers/forge!2564
2020-03-01 04:16:22 +00:00
Michael Kamensky
86a9cb13d9 Merge branch 'WAR_JP_PW' into 'master'
WAR with Japanese Planeswalkers added (option 2)

Closes #1305

See merge request core-developers/forge!2566
2020-03-01 04:15:18 +00:00
Tim Mocny
e1138ff7b5 WAR with Japanese Planeswalkers added (option 2) 2020-03-01 04:15:17 +00:00
Michael Kamensky
52fe82b834 Merge branch 'sliverfixes' into 'master'
Forum fixes Leap Day

See merge request core-developers/forge!2571
2020-03-01 04:14:47 +00:00
Tim Mocny
15e3c4f5e5 Forum fixes Leap Day 2020-03-01 04:14:47 +00:00
Michael Kamensky
4e5853e29a Merge branch 'triggerFixStack' into 'master'
GameAction: fix Trigger for Stack

Closes #1309

See merge request core-developers/forge!2572
2020-03-01 04:14:44 +00:00
Hans Mackowiak
34d0d143c5 GameAction: fix Trigger for Stack 2020-03-01 04:14:44 +00:00
Michael Kamensky
d37cb22698 Merge branch 'master' into 'master'
Added puzzle PS_THB5 + AI logic improvement for Kenrith, the Returned King

See merge request core-developers/forge!2568
2020-03-01 04:13:57 +00:00
Anthony Calosa
b5787407b6 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-03-01 11:10:55 +08:00
Anthony Calosa
4d1d79098d Better Icon Render 2020-03-01 10:47:48 +08:00
Sol
3fc0c1e20f Merge branch 'fixes' into 'master'
Fixes

See merge request core-developers/forge!2570
2020-03-01 02:44:30 +00:00
Jamin Collins
726b8bfbe5 correct sprite load progress bar to 100% max
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-03-01 02:44:30 +00:00
Anthony Calosa
0306ab97f1 Fix Crash on Desktop, Menu Arrangement, Added some Icons 2020-03-01 07:42:20 +08:00
Jamin Collins
083df572b8 Merge branch 'tinyban' into 'master'
Tiny Leaders ban - Najeela

See merge request core-developers/forge!2569
2020-02-29 21:50:28 +00:00
Tim Mocny
12c705476d Tiny Leaders ban - Najeela 2020-02-29 21:50:28 +00:00
Agetian
4880d70129 - Simple modification for AI PumpAll on Kenrith 2020-02-29 19:33:35 +03:00
Agetian
f49d5e2352 - Better AI targeting for Kenrith, the Returned King's {4}{B} ability. 2020-02-29 19:25:24 +03:00
Agetian
f8b9a31bf2 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2020-02-29 17:06:04 +03:00
Michael Kamensky
808a43b44d Merge branch 'editions28feb' into 'master'
Editions support for Grand Prix Promos, Unsanctioned

See merge request core-developers/forge!2562
2020-02-29 14:02:09 +00:00
Tim Mocny
f2c523c267 Editions support for Grand Prix Promos, Unsanctioned 2020-02-29 14:02:09 +00:00
Michael Kamensky
b847f7f8c9 Merge branch 'immolation' into 'master'
Mask of Immolation typos fix

See merge request core-developers/forge!2560
2020-02-29 14:01:26 +00:00
Tim Mocny
a9f13ee503 Mask of Immolation typos fix 2020-02-29 14:01:26 +00:00
Agetian
e0b7ea4960 - Added puzzle PS_THB5. 2020-02-29 16:49:53 +03:00
Sol
a41c0454fa Merge branch 'update-modern-banned' into 'master'
updating the Modern banned list

See merge request core-developers/forge!2565
2020-02-29 04:39:58 +00:00
Jamin W. Collins
226716c816 updating Pioneer ban list
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-02-28 19:18:59 -07:00
Jamin W. Collins
3c6f82294b updating the Modern banned list
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2020-02-28 19:05:27 -07:00
Anthony Calosa
600d672b4a Update Sprite Icons on Mobile Forge
(Default theme)
2020-02-29 05:05:51 +08:00
Anthony Calosa
48cb11566f Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-02-28 23:16:14 +08:00
Anthony Calosa
c3be51c117 Fix Missing Translation on Planar Conquest 2020-02-28 23:15:29 +08:00
Hans Mackowiak
841808d99a Merge branch 'combo_attack' into 'master'
Band Together / Combo Attack

See merge request core-developers/forge!2551
2020-02-28 15:12:42 +00:00
Tim Mocny
258f0a2072 Band Together / Combo Attack 2020-02-28 15:12:42 +00:00
Michael Kamensky
0b2e60b0c1 Merge branch 'maddening_imp' into 'master'
Maddening Imp round 2

See merge request core-developers/forge!2559
2020-02-28 05:11:31 +00:00
Tim Mocny
669bc9ceaa Maddening Imp round 2 2020-02-28 05:11:31 +00:00
Michael Kamensky
93d9d12879 Merge branch 'maddening_imp' into 'master'
Today's fixes

See merge request core-developers/forge!2558
2020-02-27 04:34:47 +00:00
Tim Mocny
9d0f3f325b Today's fixes 2020-02-27 04:34:47 +00:00
Michael Kamensky
d66b61f0a3 Merge branch 'quenchable_fire' into 'master'
Quenchable Fire - Ready!

Closes #580

See merge request core-developers/forge!2533
2020-02-26 20:01:06 +00:00
Tim Mocny
9d79b6c262 Quenchable Fire - Ready! 2020-02-26 20:01:06 +00:00
Michael Kamensky
7721b227cb Merge branch '24Feb2020' into 'master'
Nahiri, the Lithomancer +2 ability (cosmetic only)

See merge request core-developers/forge!2554
2020-02-26 19:58:08 +00:00
Tim Mocny
d1106f8df7 Nahiri, the Lithomancer +2 ability (cosmetic only) 2020-02-26 19:58:07 +00:00
Michael Kamensky
794fcdafe5 Merge branch 'sld51' into 'master'
SLD - IWD 2020 cards

See merge request core-developers/forge!2557
2020-02-26 19:57:31 +00:00
Tim Mocny
344126d5aa SLD - IWD 2020 cards 2020-02-26 19:57:31 +00:00
Anthony Calosa
2a72a87ef4 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-02-26 23:42:42 +08:00
Michael Kamensky
7b1af4035d Merge branch 'attachNPEfix' into 'master'
AttachEffect: ChooseAnObject if list is empty and chosen is null, don't add to list

See merge request core-developers/forge!2556
2020-02-26 11:05:24 +00:00
Hans Mackowiak
931d08d3ac AttachEffect: ChooseAnObject if list is empty and chosen is null, don't add to list 2020-02-26 11:05:23 +00:00
CCTV-1
85b2409a66 Merge remote-tracking branch 'upstream/master' 2020-02-26 17:52:04 +08:00
Michael Kamensky
ea582088a6 Merge branch 'newBranch' into 'master'
Updated Sprite Buttons on Mobile Forge

See merge request core-developers/forge!2552
2020-02-26 04:00:34 +00:00
Sol
468dd46205 Merge branch 'bronzetablet' into 'master'
Bronze Tablet

Closes #37

See merge request core-developers/forge!2493
2020-02-25 16:39:46 +00:00
Tim Mocny
b03f263fce Bronze Tablet 2020-02-25 16:39:45 +00:00
Anthony Calosa
15b69d3d70 Merge remote-tracking branch 'remotes/core/master' into newBranch 2020-02-26 00:07:16 +08:00
Hans Mackowiak
8004892058 Merge branch '1298-wrong-player-controls-etb-trigger-of-clone' into 'master'
Resolve "Wrong player controls ETB trigger of Clone"

Closes #1302 and #1298

See merge request core-developers/forge!2553
2020-02-25 15:03:21 +00:00
Hans Mackowiak
f03e386399 Resolve "Wrong player controls ETB trigger of Clone" 2020-02-25 15:03:20 +00:00
CCTV-1
5b9d19cfce fix unused import 2020-02-25 21:28:55 +08:00
CCTV-1
e7d5700dac update translation 2020-02-25 21:14:16 +08:00
CCTV-1
5a133686ec translate online game message 2020-02-25 21:14:04 +08:00
CCTV-1
58df46261e update translations 2020-02-25 19:00:25 +08:00
CCTV-1
9bf7e08f01 translate import deck confirm dialog 2020-02-25 19:00:03 +08:00
Anthony Calosa
40478a45ac Update Icons 2020-02-25 18:43:57 +08:00
CCTV-1
3c8b8a04e1 update translation 2020-02-25 12:24:32 +08:00
CCTV-1
82e520ca67 translate desktop sideboard dialog message 2020-02-25 12:24:22 +08:00
Anthony Calosa
01a17b4b2c Upscaled Zone Icons and FlipCard Icons 2020-02-25 12:23:04 +08:00
friarsol
63f958fa60 Some cleanup 2020-02-24 20:56:59 -05:00
CCTV-1
f3d739a5d8 update translation 2020-02-24 20:06:43 +08:00
CCTV-1
453f14760c translate desktop phase tooltip 2020-02-24 20:06:34 +08:00
CCTV-1
9f19b3f93f translate desktop deck import 2020-02-24 20:06:04 +08:00
CCTV-1
cc5d2c47ee translate some text 2020-02-24 20:05:44 +08:00
Michael Kamensky
c916c4da31 Merge branch 'tempest_efreet' into 'master'
Tempest Efreet

Closes #115

See merge request core-developers/forge!2550
2020-02-24 09:01:45 +00:00
Tim Mocny
9e79d7810f Tempest Efreet 2020-02-24 09:01:44 +00:00
Michael Kamensky
0dbbcd4edc Merge branch 'newBranch' into 'master'
Upscaled Start Button

See merge request core-developers/forge!2549
2020-02-24 09:00:58 +00:00
Anthony Calosa
eb039b3bdd Update Start Button 2020-02-23 22:38:33 +08:00
CCTV-1
f4dfa6038c fix translation format 2020-02-23 21:10:22 +08:00
Michael Kamensky
9be6a5a6a3 Merge branch 'newBranch' into 'master'
Add Support for HD Buttons on Mobile Forge

See merge request core-developers/forge!2547
2020-02-23 12:44:06 +00:00
Anthony Calosa
81c06f230a Add Support for Upscaling Buttons on Mobile Forge 2020-02-23 19:10:11 +08:00
Michael Kamensky
645a11f9c3 Merge branch 'master' into 'master'
Initial logic implementation for Mercenaries

See merge request core-developers/forge!2546
2020-02-23 05:19:38 +00:00
Agetian
68ca9ec622 - Initial logic implementation for Mercenaries 2020-02-23 08:14:48 +03:00
Michael Kamensky
b4883eb77e Merge branch 'mercenaries' into 'master'
Mercenaries!

See merge request core-developers/forge!2545
2020-02-23 04:39:16 +00:00
Tim Mocny
7e57ffb62d Mercenaries! 2020-02-23 04:39:14 +00:00
Michael Kamensky
820666b278 Merge branch 'fumble' into 'master'
Fumble - Ready!

See merge request core-developers/forge!2535
2020-02-23 04:31:23 +00:00
Tim Mocny
9d0c2968aa Fumble - Ready! 2020-02-23 04:31:23 +00:00
Michael Kamensky
d3f2b07c83 Merge branch 'emergencypowers' into 'master'
Emergency Powers Addendum

See merge request core-developers/forge!2544
2020-02-23 04:31:04 +00:00
Tim Mocny
57ebad948d Emergency Powers Addendum 2020-02-23 04:31:04 +00:00
Michael Kamensky
3f1b9df62a Merge branch '1290-trigger-needs-cleanup-after-cost-is-paid' into 'master'
Resolve "Trigger needs cleanup after cost is paid"

Closes #1290

See merge request core-developers/forge!2543
2020-02-22 14:46:44 +00:00
Hans Mackowiak
f990f19795 Resolve "Trigger needs cleanup after cost is paid" 2020-02-22 14:46:44 +00:00
Michael Kamensky
b2e1855949 Merge branch '1295-runtimeexception-abilityfactory-getability-startled-awake-has-no-svar-trigbounce' into 'master'
Resolve "RuntimeException: AbilityFactory : getAbility -- Startled Awake has no SVar: TrigBounce"

Closes #1295

See merge request core-developers/forge!2540
2020-02-22 11:10:19 +00:00
Hans Mackowiak
850e54fbbc Resolve "RuntimeException: AbilityFactory : getAbility -- Startled Awake has no SVar: TrigBounce" 2020-02-22 11:10:19 +00:00
Michael Kamensky
9931b33be7 Merge branch '1294-anax-hardened-in-the-forge-power-abnormal' into 'master'
Resolve "'Anax, Hardened in the Forge' power abnormal"

Closes #1294

See merge request core-developers/forge!2542
2020-02-22 10:17:40 +00:00
Hans Mackowiak
040518d094 Resolve "'Anax, Hardened in the Forge' power abnormal" 2020-02-22 10:17:40 +00:00
Michael Kamensky
823c651532 Merge branch 'master' into 'master'
Added puzzle PS_THB4 - Possibility Storm - Theros Beyond Death 04

See merge request core-developers/forge!2541
2020-02-22 10:17:32 +00:00
Agetian
b412a361b9 - Added puzzle PS_THB4. 2020-02-22 11:33:32 +03:00
Michael Kamensky
022ea1bb75 Merge branch 'PSLD1292' into 'master'
Secret Lair Promos.txt update

Closes #1292

See merge request core-developers/forge!2538
2020-02-22 06:33:58 +00:00
Tim Mocny
f08f7b09d5 Secret Lair Promos.txt update 2020-02-22 06:33:58 +00:00
Michael Kamensky
16452dd11a Merge branch 'tezzeret21Feb' into 'master'
Forum fixes

See merge request core-developers/forge!2539
2020-02-22 06:33:45 +00:00
Tim Mocny
b0791a0c6a Forum fixes 2020-02-22 06:33:45 +00:00
friarsol
2b8d826209 I think this should be working now! 2020-02-21 22:26:33 -05:00
CCTV-1
f37eee21cd update translations 2020-02-21 20:48:13 +08:00
CCTV-1
e195fe40b9 translate some message text 2020-02-21 20:47:38 +08:00
Michael Kamensky
900ba9ec5b Merge branch '1276-display-divided-value' into 'master'
Display Divided Values in stack description

Closes #1276

See merge request core-developers/forge!2524
2020-02-21 04:08:16 +00:00
CCTV-1
b2526523d8 fix key typo 2020-02-20 18:48:08 +08:00
Michael Kamensky
8db0281f91 Merge branch '559' into 'master'
Search zones for any number of cards named X and exile (or something else)

Closes #559

See merge request core-developers/forge!2527
2020-02-20 08:43:39 +00:00
Tim Mocny
9637091ab5 Search zones for any number of cards named X and exile (or something else) 2020-02-20 08:43:39 +00:00
Michael Kamensky
45771ec8c0 Merge branch 'mausoleum_wanderer' into 'master'
Mausoleum Wanderer proofread

See merge request core-developers/forge!2532
2020-02-20 08:42:39 +00:00
Tim Mocny
5c3ff8297e Mausoleum Wanderer proofread 2020-02-20 08:42:39 +00:00
Michael Kamensky
722f512e53 Merge branch '504-shared-fate-can-not-cast-spell-queller-via-flash' into 'master'
Resolve "Shared Fate -- can not cast Spell Queller via Flash"

Closes #504

See merge request core-developers/forge!2534
2020-02-20 08:42:33 +00:00
Michael Kamensky
0cceaf0485 Merge branch '1286' into 'master'
Challenger 2020 Precon Decks (Quest Mode)

Closes #1286

See merge request core-developers/forge!2531
2020-02-20 08:40:55 +00:00
Tim Mocny
23d0d70478 Challenger 2020 Precon Decks (Quest Mode) 2020-02-20 08:40:55 +00:00
Hans Mackowiak
2eea49a8e4 Spell: add getAlternateHost for spells that would change the CardState 2020-02-20 07:03:24 +01:00
CCTV-1
c96ded2508 update translations 2020-02-20 10:15:23 +08:00
CCTV-1
f0a6760312 translate quest starting pool type 2020-02-20 10:15:08 +08:00
CCTV-1
55db217c74 update translations 2020-02-19 21:19:14 +08:00
CCTV-1
11c5283014 translate some desktop quest text 2020-02-19 21:18:58 +08:00
CCTV-1
bad3227e4f update translation 2020-02-19 11:37:49 +08:00
CCTV-1
7ea0a5268c translate deck viewer 2020-02-19 11:37:26 +08:00
CCTV-1
456083d2b0 translate tip text 2020-02-19 11:36:52 +08:00
CCTV-1
ad0766d9a6 Merge remote-tracking branch 'upstream/master' 2020-02-19 11:35:53 +08:00
CCTV-1
e1dbdcc051 update translations 2020-02-19 08:34:13 +08:00
Michael Kamensky
a7d8a62a69 Merge branch '1288-unstable-shapeshifter-not-unstable' into 'master'
Resolve "Unstable Shapeshifter not unstable"

Closes #1288

See merge request core-developers/forge!2530
2020-02-18 17:03:10 +00:00
Hans Mackowiak
c8fdc6f272 Resolve "Unstable Shapeshifter not unstable" 2020-02-18 17:03:10 +00:00
Sol
9990062ec1 Merge branch '1281-general-ui-display-commander-tax' into 'master'
Resolve "[General / UI] Display Commander Tax"

Closes #1281

See merge request core-developers/forge!2528
2020-02-18 14:23:48 +00:00
Hans Mackowiak
6817b4af66 Resolve "[General / UI] Display Commander Tax" 2020-02-18 14:23:48 +00:00
friarsol
133f1656d8 Some more progress with autoupdate button 2020-02-17 21:51:18 -05:00
friarsol
599d75351c Hook up AutoUpdater to do its thing 2020-02-17 16:51:34 -05:00
Hans Mackowiak
c42202a140 Merge branch 'Hanmac-master-patch-75614' into 'master'
Anax, Hardened in the Forge: updated triggers, need two for itself and other

See merge request core-developers/forge!2496
2020-02-16 15:36:07 +00:00
Hans Mackowiak
d6c0cf6ef8 Anax, Hardened in the Forge: updated triggers, need two for itself and other 2020-02-16 15:36:07 +00:00
MS
72ad7804ca Update CountersPutEffect.java 2020-02-16 07:41:06 +00:00
CCTV-1
11e33bfaa0 update translation 2020-02-16 13:28:35 +08:00
CCTV-1
b1ad316134 translate some tab/menu text 2020-02-16 13:28:16 +08:00
CCTV-1
f2dc8e3e80 update translation 2020-02-16 12:02:10 +08:00
CCTV-1
3ff20effc8 translate floating zone title 2020-02-16 12:01:20 +08:00
CCTV-1
eb4ae865d4 Merge remote-tracking branch 'upstream/master' 2020-02-16 12:00:01 +08:00
Michael Kamensky
6bf1af44d8 Merge branch 'master' into 'master'
Added puzzle PS_THB3 - Possibility Storm - Theros Beyond Death 03

See merge request core-developers/forge!2529
2020-02-15 19:13:40 +00:00
Agetian
a74f1a1427 - Added puzzle PS_THB3. 2020-02-15 16:28:36 +03:00
Michael Kamensky
df88cb970f Merge branch '13Feb2020' into 'master'
Quest Shop Precons - Commander 2018

Closes #205

See merge request core-developers/forge!2525
2020-02-15 03:51:04 +00:00
Tim Mocny
452dee7e11 Quest Shop Precons - Commander 2018 2020-02-15 03:51:04 +00:00
Michael Kamensky
f6a6c33193 Merge branch '14Feb2020SLD' into 'master'
SLD - Theros Stargazing

See merge request core-developers/forge!2526
2020-02-15 03:50:36 +00:00
Tim Mocny
6a98c8f371 SLD - Theros Stargazing 2020-02-15 03:50:35 +00:00
Michael Kamensky
9546594e8d Merge branch 'target-other-zones' into 'master'
Show ZoneAreas when targeting non-Stack zones

See merge request core-developers/forge!2492
2020-02-14 15:01:02 +00:00
Michael Kamensky
23f5b7696a Merge branch 'kessCancelFix' into 'master'
HumanPlaySpellAbility: fix CastSA and CastFrom reset when ability is canceld

Closes #1282

See merge request core-developers/forge!2523
2020-02-14 14:01:16 +00:00
Hans Mackowiak
93ae66ad39 HumanPlaySpellAbility: fix CastSA and CastFrom reset when ability is canceld 2020-02-14 14:01:16 +00:00
ESE_Manuel
4d3ce706a5 workaround for divideMap not cleared after canceling ability 2020-02-14 12:50:52 +01:00
ESE_Manuel
5b57ef3531 Revert "fixed bug, dividedMap clear"
This reverts commit eadc0afe1a.
2020-02-14 12:06:02 +01:00
ESE_Manuel
eadc0afe1a fixed bug, dividedMap clear
needs to be cleared when SA was not put on the stack or wrong stack description
2020-02-14 11:38:09 +01:00
ESE_Manuel
687b5f18ef made stack description more clear for DamageDealEffect
changed stack description for distribute parameter (x counter)
2020-02-14 10:54:58 +01:00
ESE_Manuel
25178869ae fallback if there is no targetrestriction on divide damage effects 2020-02-14 10:09:52 +01:00
ESE_Manuel
eedcad8280 removed unused import 2020-02-14 08:32:24 +01:00
ESE_Manuel
50c94755fb Display Divided Values in stack description 2020-02-14 07:58:19 +01:00
friarsol
ec79434759 Improve Android zone restoration 2020-02-13 23:17:26 -05:00
Michael Kamensky
4192d56909 Merge branch 'mana_symbols_vanish' into 'master'
changed hardcoded symbol WIDTH and HEIGHT to constants

See merge request core-developers/forge!2520
2020-02-14 03:26:08 +00:00
Michael Kamensky
2c82aedb74 Merge branch 'bling' into 'master'
Secret Lair Drop Series and Promos

See merge request core-developers/forge!2522
2020-02-13 18:15:02 +00:00
Tim Mocny
049ed5f9c4 Secret Lair Drop Series and Promos 2020-02-13 18:15:02 +00:00
Michael Kamensky
b183862e8d Merge branch 'triggerDamageAll' into 'master'
TriggerDamageAll: add new Trigger for Mindblade Render

See merge request core-developers/forge!2519
2020-02-13 15:12:52 +00:00
Hans Mackowiak
57bc3f930a TriggerDamageAll: add new Trigger for Mindblade Render 2020-02-13 15:12:52 +00:00
Michael Kamensky
48a3b4d652 Merge branch '1272-battle-lands-etb-tap-untap-issue' into 'master'
Resolve "Battle Lands ETB tap/untap issue"

Closes #1272

See merge request core-developers/forge!2521
2020-02-13 15:12:03 +00:00
Hans Mackowiak
bee93cbab3 Player: update LastState when Land is played 2020-02-13 10:29:00 +00:00
Michael Kamensky
9d0a683db8 Merge branch 'release-updates' into 'master'
Update some release files

See merge request core-developers/forge!2516
2020-02-13 05:55:16 +00:00
Michael Kamensky
e163132a1e Merge branch 'bling' into 'master'
Bling cards

See merge request core-developers/forge!2517
2020-02-13 05:54:32 +00:00
Tim Mocny
d35cb8de32 Bling cards 2020-02-13 05:54:31 +00:00
Michael Kamensky
162a27f5a1 Merge branch '12Feb2020' into 'master'
Today's tweaks

See merge request core-developers/forge!2514
2020-02-13 05:53:48 +00:00
Tim Mocny
d1c2096024 Today's tweaks 2020-02-13 05:53:48 +00:00
Zimtente
50c8e129d0 changed hardcoded symbol WIDTH and HEIGHT to constants 2020-02-12 19:07:45 +01:00
Sol
01bd5e5d54 Update CONTRIBUTORS.txt 2020-02-12 18:05:09 +00:00
Sol
562a9e1f8d Update ANNOUNCEMENTS.txt 2020-02-12 18:04:34 +00:00
Sol
548811b370 Merge branch 'mana_symbols_vanish' into 'master'
fixed mana symbols vanishing/not being in the right place in card descriptions

See merge request core-developers/forge!2515
2020-02-12 17:59:25 +00:00
Zimtente
6ae29a311a fixed mana symbols vanishing/not being in the right place in card descriptions 2020-02-12 17:40:50 +01:00
Sol
9934e20f8b Merge branch 'promoarts' into 'master'
Promo arts – correcting a few errors

See merge request core-developers/forge!2513
2020-02-12 14:43:40 +00:00
Tim Mocny
e7a5ee57f1 Promo arts – correcting a few errors 2020-02-12 14:43:40 +00:00
Michael Kamensky
06f2f416e0 Merge branch 'brawl_update' into 'master'
Brawl format update

Closes #1277

See merge request core-developers/forge!2511
2020-02-12 04:24:38 +00:00
Tim Mocny
8bda30179e Brawl format update 2020-02-12 04:24:38 +00:00
Michael Kamensky
9806c2c63c Merge branch 'promoarts' into 'master'
Promo arts update (THB, ELD)

See merge request core-developers/forge!2512
2020-02-12 04:24:23 +00:00
Tim Mocny
9d5d6dd6d2 Promo arts update (THB, ELD) 2020-02-12 04:24:23 +00:00
Michael Kamensky
8fecf07c47 Merge branch '7Feb2020' into 'master'
2/10/2020 tweaks

See merge request core-developers/forge!2508
2020-02-11 03:12:46 +00:00
Tim Mocny
a539d9bf5a 2/10/2020 tweaks 2020-02-11 03:12:46 +00:00
friarsol
ddc531c5f2 Updates for Android 2020-02-10 21:30:19 -05:00
CCTV-1
102b366c1e update translation 2020-02-10 20:24:11 +08:00
CCTV-1
26fb509cb9 translate FFileChooser 2020-02-10 20:24:01 +08:00
CCTV-1
81e616ad16 progress start with 1 2020-02-10 20:23:25 +08:00
CCTV-1
c140ae7647 Merge remote-tracking branch 'upstream/master' 2020-02-10 20:21:23 +08:00
friarsol
ef0a1a843d Pop up other zones during targeting 2020-02-09 21:32:24 -05:00
Michael Kamensky
bf200de73b Merge branch 'triggerFixChangedCopy' into 'master'
Trigger: fix copy need new Map for params

See merge request core-developers/forge!2507
2020-02-09 19:34:03 +00:00
Hans Mackowiak
9a56fb8998 Trigger: fix copy need new Map for params 2020-02-09 19:34:03 +00:00
Michael Kamensky
4a3740dac7 Merge branch 'staticAbilitySuspendedFix' into 'master'
StaticAbility: fix ability is removed by other ability

See merge request core-developers/forge!2505
2020-02-08 17:33:05 +00:00
Hans Mackowiak
31145ef2eb StaticAbility: fix ability is removed by other ability 2020-02-08 17:33:04 +00:00
Michael Kamensky
23ab256f7d Merge branch 'gameActionCloneFix' into 'master'
GameAction: fix Clone working again

See merge request core-developers/forge!2506
2020-02-08 17:33:00 +00:00
Hans Mackowiak
0f435b58a1 GameAction: fix Clone working again 2020-02-08 18:17:21 +01:00
Michael Kamensky
5d1a0421fd Merge branch 'fixAddTypeChosen' into 'master'
StaticAbilityContinuous: fix AddType ChosenType

See merge request core-developers/forge!2504
2020-02-08 04:44:45 +00:00
Hans Mackowiak
55ed4c1bc0 StaticAbilityContinuous: fix AddType ChosenType 2020-02-08 04:44:45 +00:00
Michael Kamensky
22a6a72f12 Merge branch 'master' into 'master'
Added puzzle PS_THB2 - Possibility Storm - Theros Beyond Death 02

See merge request core-developers/forge!2503
2020-02-07 18:14:55 +00:00
Agetian
7c38ac7dfa - Fixed scripted targeting in GameState not remembering targets when necessary.
- Added puzzle PS_THB2.
2020-02-07 20:54:36 +03:00
Michael Kamensky
d29dc72831 Merge branch 'fixesGrafCage' into 'master'
Fixes graf cage

Closes #1195

See merge request core-developers/forge!2377
2020-02-07 17:29:54 +00:00
Hans Mackowiak
e062c83c02 Fixes graf cage 2020-02-07 17:29:54 +00:00
Hans Mackowiak
c25971bba9 Merge branch '6Feb2020' into 'master'
tawnos_urzas_apprentice.txt UI tweaks ONLY

See merge request core-developers/forge!2500
2020-02-07 04:58:58 +00:00
Tim Mocny
92a11d801c tawnos_urzas_apprentice.txt UI tweaks ONLY 2020-02-07 04:58:57 +00:00
CCTV-1
0ce636c6b4 Merge remote-tracking branch 'upstream/master' 2020-02-07 12:37:45 +08:00
CCTV-1
65969d4ae2 update translation 2020-02-07 12:31:48 +08:00
CCTV-1
0ba3cdfe87 translate mobile achievements title 2020-02-07 12:31:27 +08:00
CCTV-1
5dd0edb317 translate Statistics 2020-02-07 12:30:42 +08:00
CCTV-1
2bb6379232 translate desktop some DragTab 2020-02-07 12:30:06 +08:00
CCTV-1
ebdd532343 translate group 2020-02-07 12:27:57 +08:00
CCTV-1
ac97774639 translate achievement menu 2020-02-07 12:27:38 +08:00
CCTV-1
f6f690ae01 Delve message show exile progress 2020-02-07 12:27:04 +08:00
Michael Kamensky
87e6b38fa5 Merge branch 'patch-8' into 'master'
Update new translations es-ES.properties

See merge request core-developers/forge!2491
2020-02-07 04:09:34 +00:00
Michael Kamensky
2dcc56934d Merge branch 'ashiok' into 'master'
Improve cleanup on ashiok

See merge request core-developers/forge!2490
2020-02-07 04:07:39 +00:00
Michael Kamensky
f5cb80cec5 Merge branch 'magicStackSpellAbilityFix' into 'master'
Magic stack spell ability fix

See merge request core-developers/forge!2501
2020-02-07 04:06:55 +00:00
Hans Mackowiak
1d71b1efea Magic stack spell ability fix 2020-02-07 04:06:54 +00:00
Michael Kamensky
62e206f8ed Merge branch 'effectUntilCounterRemoved' into 'master'
Refactor: Effects with "For as long" it has CounterType

Closes #1271

See merge request core-developers/forge!2494
2020-02-07 04:06:25 +00:00
Hans Mackowiak
0de140538c Refactor: Effects with "For as long" it has CounterType 2020-02-07 04:06:24 +00:00
Michael Kamensky
55167c5dca Merge branch 'master' into 'master'
Fix AI for Domri's Ambush

Closes #1273

See merge request core-developers/forge!2499
2020-02-06 18:17:56 +00:00
Michael Kamensky
9c78dbfde4 Merge branch 'pioneerdeckgenthb' into 'master'
Updated pioneer and standard data for theros metagame

See merge request core-developers/forge!2498
2020-02-06 17:47:15 +00:00
Agetian
0ca0458656 - Fix AI for Domri's Ambush.
- Don't reset targets on DamageDealAi if it's parented to an ability with valid number of targets set.
2020-02-06 20:33:56 +03:00
austinio7116
1b86738d10 Updated pioneer and standard data for theros metagame
(cherry picked from commit 4b503cb)
2020-02-06 11:27:52 +00:00
swordshine
ccfbaf5b3d Merge branch '5Feb2020' into 'master'
2/5/2020 fixes

See merge request core-developers/forge!2497
2020-02-06 09:37:18 +00:00
Tim Mocny
16ecae29f0 2/5/2020 fixes 2020-02-06 09:37:18 +00:00
Michael Kamensky
7a9fb7a4d8 Merge branch '4Feb2020' into 'master'
Today's tweaks

See merge request core-developers/forge!2495
2020-02-05 03:45:14 +00:00
Tim Mocny
aef84aed51 Today's tweaks 2020-02-05 03:45:14 +00:00
klaxnek
a8b1ce3c32 Update es-ES.properties 2020-02-04 20:39:51 +00:00
klaxnek
7dde40beff Update new translations es-ES.properties 2020-02-03 20:15:15 +00:00
Adam Pantel
ccf39adde4 Improve cleanup on ashiok 2020-02-03 14:24:48 -05:00
Michael Kamensky
5b37a6fb53 Merge branch 'rebirth' into 'master'
Rebirth

See merge request core-developers/forge!2487
2020-02-03 16:00:50 +00:00
Tim Mocny
942225291f Rebirth 2020-02-03 16:00:50 +00:00
Michael Kamensky
4787784d15 Merge branch 'master' into 'master'
translate some text and fix Localizer

Closes #1270

See merge request core-developers/forge!2450
2020-02-03 15:05:28 +00:00
Sol
093f157f77 Merge branch 'master' into 'master'
MillAI logic for Rebirth.

See merge request core-developers/forge!2489
2020-02-03 14:55:17 +00:00
Agetian
78f3614c0a - Smaller life threshold for AILogic Rebirth. 2020-02-03 17:49:01 +03:00
Agetian
ae060a1b76 - MillAI logic for Rebirth. 2020-02-03 17:29:46 +03:00
Michael Kamensky
244b3d6d24 Merge branch 'staticKeywordChangeList' into 'master'
refactor Keywords using Chosen Values

See merge request core-developers/forge!2462
2020-02-03 11:35:28 +00:00
Hans Mackowiak
01bf10c719 refactor Keywords using Chosen Values 2020-02-03 11:35:28 +00:00
CCTV-1
5718072f60 fix miss call Localizer.getInstance().getMessage 2020-02-03 19:31:57 +08:00
CCTV-1
1dfd977238 fix compile error 2020-02-03 18:48:30 +08:00
CCTV-1
9030a39d1d update translation 2020-02-03 18:43:10 +08:00
CCTV-1
d74a4143f3 translate mulligan log 2020-02-03 18:42:35 +08:00
CCTV-1
f6509dd30e Merge remote-tracking branch 'upstream/master' 2020-02-03 18:40:13 +08:00
Michael Kamensky
6c512dd451 Merge branch 'allBasicLandType' into 'master'
All basic land type and AdjustLandPlays

See merge request core-developers/forge!2467
2020-02-03 10:09:45 +00:00
Hans Mackowiak
00c83f1d25 All basic land type and AdjustLandPlays 2020-02-03 10:09:45 +00:00
Michael Kamensky
28615040e8 Merge branch '1269-momir-basic-mode-broken' into 'master'
Resolve "Momir Basic Mode Broken"

Closes #1269

See merge request core-developers/forge!2488
2020-02-03 09:44:42 +00:00
Hans Mackowiak
0482d4636b Resolve "Momir Basic Mode Broken" 2020-02-03 09:44:42 +00:00
Michael Kamensky
86de537fcb Merge branch 'keywordLanguageChange' into 'master'
Keyword: fix problems with other languages

See merge request core-developers/forge!2485
2020-02-03 05:03:29 +00:00
Hans Mackowiak
fb7fdb65f2 Keyword: fix problems with other languages 2020-02-03 05:03:29 +00:00
Sol
15c36118d6 Merge branch '2Feb2020' into 'master'
little Elspeth PW deck edition tweak

See merge request core-developers/forge!2486
2020-02-03 02:26:43 +00:00
Tim Mocny
092eae52fe little Elspeth PW deck edition tweak 2020-02-03 02:26:43 +00:00
CCTV-1
ac58058938 escape single quotes 2020-02-03 09:26:58 +08:00
CCTV-1
64117ad1fc update translation 2020-02-02 19:48:56 +08:00
Michael Kamensky
317833af6d Merge branch 'master' into 'master'
Added puzzle PS_THB1 (both variants A and B)

See merge request core-developers/forge!2484
2020-02-02 10:25:30 +00:00
Agetian
54d367ae8c - Added puzzle PS_THB1 (both variants A and B). 2020-02-02 10:36:01 +03:00
CCTV-1
1381de7d55 add comment 2020-02-02 09:14:43 +08:00
CCTV-1
caacde3aad non creature permanent effect translation 2020-02-02 09:14:12 +08:00
CCTV-1
49e55f1326 update translation 2020-02-02 08:48:53 +08:00
CCTV-1
5082da429f log formatter full translation 2020-02-02 08:48:34 +08:00
CCTV-1
ea4e821f51 Merge remote-tracking branch 'upstream/master' 2020-02-02 08:43:42 +08:00
Sol
83c0195648 Merge branch '31Jan2020' into 'master'
Today's tweaks

See merge request core-developers/forge!2483
2020-02-01 03:58:34 +00:00
Tim Mocny
5c465f8bf0 Today's tweaks 2020-02-01 03:58:34 +00:00
Michael Kamensky
c670d175d9 Merge branch 'master' into 'master'
Check to make sure the cards are targetable by AI in DamageDealAi (mandatory).

See merge request core-developers/forge!2481
2020-01-31 14:38:37 +00:00
Agetian
415e2a1623 - Remove the unneeded mandatory parameter. 2020-01-31 16:54:29 +03:00
Agetian
dff5962b8e - Check to make sure the cards are targetable by AI. 2020-01-31 15:05:56 +03:00
Michael Kamensky
d7febae2e1 Merge branch 'master' into 'master'
Improved DamageDealAi and DrawAi

Closes #1267

See merge request core-developers/forge!2480
2020-01-31 10:20:22 +00:00
Michael Kamensky
a757e033d9 Merge branch 'triggerRunParams' into 'master'
Trigger: remove runParams from Trigger

See merge request core-developers/forge!2479
2020-01-31 10:15:31 +00:00
Hans Mackowiak
c3b05157c5 Trigger: remove runParams from Trigger 2020-01-31 10:15:31 +00:00
Agetian
cb3430b806 - Remove the unneeded mandatory check 2020-01-31 13:08:01 +03:00
Agetian
a86dad40c3 - DamageDealAi: target own indestructible stuff when able (if mandatory and has no better targets) 2020-01-31 12:46:32 +03:00
Agetian
04066b9bc2 - Fix DrawAi not accounting for pay costs on trigger 2020-01-31 12:34:05 +03:00
Agetian
6580883ccc - Improved DamageDealAi for mandatory triggers requiring a choice of planeswalker (e.g. Chandra, Artisan of Fire vs. a Hexproof opponent) 2020-01-31 09:58:51 +03:00
CCTV-1
fa30bbe67c Merge remote-tracking branch 'upstream/master' 2020-01-31 09:10:46 +08:00
CCTV-1
e39b70a5d8 update translation 2020-01-31 09:10:29 +08:00
CCTV-1
45f33106f2 display escape exile card progress 2020-01-31 09:10:01 +08:00
Michael Kamensky
415f911e46 Merge branch 'stupidideas' into 'master'
Soul Echo!

See merge request core-developers/forge!2475
2020-01-30 13:19:27 +00:00
Tim Mocny
7b4b869992 Soul Echo! 2020-01-30 13:19:26 +00:00
Michael Kamensky
3d57759d73 Merge branch 'patch-3' into 'master'
Update net-decks.txt adding Current MTGA Decks

See merge request core-developers/forge!2473
2020-01-30 13:15:26 +00:00
Michael Kamensky
ad0bec318f Merge branch '29Jan2020forumfixes' into 'master'
Various fixes reported on forum

See merge request core-developers/forge!2476
2020-01-30 13:12:24 +00:00
Tim Mocny
9571b2695f Various fixes reported on forum 2020-01-30 13:12:24 +00:00
Michael Kamensky
f09bcc4a66 Merge branch 'master' into 'master'
Fix Garruk, Cursed Huntsman achievement definition.

Closes #1262

See merge request core-developers/forge!2477
2020-01-30 13:09:39 +00:00
Agetian
fc424b1bdd - Fix Garruk, Cursed Huntsman achievement definition. 2020-01-30 14:19:13 +03:00
CCTV-1
8a9b7c562c fix lblReturnForLondon translation 2020-01-30 11:00:45 +08:00
CCTV-1
4d9c730f6f make zh-CN reminder text work(update generate script) 2020-01-29 20:16:14 +08:00
CCTV-1
9a1223c228 fix INVALID PROPERTY: 'Report' 2020-01-29 09:58:21 +08:00
CCTV-1
d7787dc974 update translation 2020-01-29 08:12:55 +08:00
CCTV-1
a3f30d899b fix missing prompt translation 2020-01-29 08:12:38 +08:00
Churrufli
ca48e75a9d Update net-decks.txt adding Current MTGA Decks 2020-01-28 22:16:33 +00:00
CCTV-1
e5051d2635 Merge remote-tracking branch 'upstream/master' 2020-01-28 22:34:10 +08:00
Michael Kamensky
c5a6145ed7 Merge branch 'patch-7' into 'master'
Mobile: AI Personality Translation

See merge request core-developers/forge!2454
2020-01-28 13:23:50 +00:00
Michael Kamensky
b2398ec8e0 Merge branch 'thbtranslation' into 'master'
Updated card translations (Theros: Beyond Death)

See merge request core-developers/forge!2471
2020-01-28 13:22:52 +00:00
Michael Kamensky
1b739ab6e5 Merge branch 'fix-thb-edition' into 'master'
Updating edition file until we have extra art ready to download

See merge request core-developers/forge!2465
2020-01-28 13:22:27 +00:00
Michael Kamensky
2e20a88b3b Merge branch '27jan2020' into 'master'
viviens_grizzly.txt fix

See merge request core-developers/forge!2470
2020-01-28 13:21:53 +00:00
Tim Mocny
9d6798e698 viviens_grizzly.txt fix 2020-01-28 13:21:53 +00:00
klaxnek
f3078059b2 Updated card translations (Theros: Beyond Death). Added Italian to script. 2020-01-28 10:05:23 +01:00
CCTV-1
fe2c3c90d6 Merge remote-tracking branch 'upstream/master' 2020-01-28 08:47:21 +08:00
Michael Kamensky
b994bce116 Merge branch 'master' into 'master'
Added puzzle PS_THB0b - Possibility Storm - Theros Beyond Death 00b (Preseason Puzzle 2)

See merge request core-developers/forge!2466
2020-01-26 16:45:38 +00:00
Agetian
f86d6fc2a8 - Added puzzle PS_THB0b. 2020-01-26 13:55:07 +03:00
CCTV-1
747fa3eb3c update translation 2020-01-26 09:17:09 +08:00
CCTV-1
f723f3347a fix INVALID PROPERTY: 'lblAbleToCastAnytingNiceBeing.' 2020-01-26 09:16:56 +08:00
friarsol
8d1367461d Updating edition file until we have extra art ready to download 2020-01-25 19:44:20 -05:00
CCTV-1
3fd3b2d642 update translation 2020-01-25 20:58:08 +08:00
CCTV-1
0f853983c9 translate some Achievements 2020-01-25 20:57:10 +08:00
CCTV-1
1d20aeddda update translation 2020-01-25 13:57:54 +08:00
CCTV-1
ba10aa935a display localization card name/type 2020-01-25 13:57:34 +08:00
CCTV-1
56c63bc6d1 translate some text 2020-01-25 13:54:32 +08:00
CCTV-1
ef70434277 update translation 2020-01-25 10:51:00 +08:00
CCTV-1
313112a488 translate some text 2020-01-25 10:50:39 +08:00
CCTV-1
9030939938 Merge remote-tracking branch 'upstream/master' 2020-01-25 09:20:03 +08:00
Michael Kamensky
c87fdb820b Merge branch 'issue1253' into 'master'
Number of colors matters cards

Closes #1253

See merge request core-developers/forge!2464
2020-01-24 15:21:09 +00:00
Tim Mocny
fd260af645 Number of colors matters cards 2020-01-24 15:21:08 +00:00
CCTV-1
f7904aac2b Merge remote-tracking branch 'upstream/master' 2020-01-24 09:43:40 +08:00
CCTV-1
c338988650 update translation 2020-01-24 09:42:12 +08:00
CCTV-1
07c4e1b57b fix translation lable typo,incorrect use of symbolLookup. 2020-01-24 09:41:38 +08:00
Michael Kamensky
a5b58aa3ae Merge branch 'patch-3' into 'master'
Update nightcreep, added missing RemoveIntrinsicAbilities

See merge request core-developers/forge!2463
2020-01-23 09:13:11 +00:00
Hans Mackowiak
31fc6e3650 Update nightcreep, added missing RemoveIntrinsicAbilities 2020-01-23 09:13:11 +00:00
CCTV-1
75dc2674d6 update translation 2020-01-23 12:54:10 +08:00
CCTV-1
f212cf1dc8 translate SleeveSelector 2020-01-23 12:53:10 +08:00
CCTV-1
0a3f1bf43c Merge remote-tracking branch 'upstream/master' 2020-01-23 12:52:10 +08:00
Michael Kamensky
1039657a82 Merge branch '22Jan2020' into 'master'
thassa_deep_dwelling.txt fix

Closes #1246

See merge request core-developers/forge!2461
2020-01-23 04:37:59 +00:00
Tim Mocny
43299a5298 thassa_deep_dwelling.txt fix 2020-01-23 04:37:59 +00:00
CCTV-1
5f8f82fc93 update Simplified Chinese characters list 2020-01-23 11:51:50 +08:00
CCTV-1
171cc23dba THB card translation 2020-01-23 11:50:12 +08:00
CCTV-1
9e5e05ae3c add ELD Scryfall missing translation 2020-01-23 11:49:55 +08:00
CCTV-1
e67b63375c update translation 2020-01-22 12:52:07 +08:00
CCTV-1
dda23854a3 translate some text 2020-01-22 12:51:44 +08:00
CCTV-1
625673e167 Merge remote-tracking branch 'upstream/master' 2020-01-22 12:48:25 +08:00
swordshine
d8845a413d Merge branch 'removeNoCyclingRule' into 'master'
Remove no cycling rule

See merge request core-developers/forge!2460
2020-01-22 03:43:53 +00:00
Hans Mackowiak
30f80f75df Remove no cycling rule 2020-01-22 03:43:53 +00:00
swordshine
c4c9d5e042 Merge branch 'master' into 'master'
Improve the AI play logic for Lotus Field.

See merge request core-developers/forge!2459
2020-01-22 03:42:08 +00:00
swordshine
b56de30ea2 Merge branch '21Jan2020' into 'master'
Psychic Possession (issue 1239)

Closes #1239

See merge request core-developers/forge!2458
2020-01-22 03:41:37 +00:00
Tim Mocny
2782046bed Psychic Possession (issue 1239) 2020-01-22 03:41:37 +00:00
Agetian
116fa84b16 - Improve the AI play logic for Lotus Field. 2020-01-21 23:02:24 +03:00
Michael Kamensky
bdb10fe59c Merge branch 'master' into 'master'
Added puzzle PS_THB0a - Possibility Storm - Theros Beyond Death 00a (preseason 1)

See merge request core-developers/forge!2457
2020-01-21 17:50:53 +00:00
Agetian
7aa93d61b3 - Added puzzle PS_THB0a. 2020-01-21 20:31:03 +03:00
CCTV-1
74ed34b6dd Merge remote-tracking branch 'upstream/master' 2020-01-21 17:22:43 +08:00
CCTV-1
faf9ace2ab update translation 2020-01-21 17:22:27 +08:00
Michael Kamensky
1602a1e78a Merge branch 'autodownload-expansion' into 'master'
Auto download from Quest WinLose screen

See merge request core-developers/forge!2456
2020-01-21 06:38:01 +00:00
Michael Kamensky
c6b0a0e072 Merge branch 'fixEnumTypes' into 'master'
Use EnumSet in Conditions/Restrictions

See merge request core-developers/forge!2455
2020-01-21 06:37:53 +00:00
Hans Mackowiak
311eaa70e3 Use EnumSet in Conditions/Restrictions 2020-01-21 06:37:53 +00:00
friarsol
659b1c7a63 Auto download from Quest WinLose screen 2020-01-20 21:55:12 -05:00
CCTV-1
c29432cef2 update translation 2020-01-20 23:46:12 +08:00
CCTV-1
7256cc2f8f translate mobile PlanarConquest and other some text 2020-01-20 23:45:49 +08:00
Sol
a9693495d7 Update mystic_sanctuary.txt 2020-01-20 04:22:24 +00:00
CCTV-1
feae4ebf81 fix:charset detect don't work 2020-01-20 11:24:20 +08:00
CCTV-1
e97ca38cbe fix var name wrong 2020-01-20 09:35:06 +08:00
CCTV-1
eff1be31da fix:in desktop,use chinese,if translation text use placeholder strings, getMessage return a garbled string(a string,the content have two encoding) 2020-01-20 09:26:52 +08:00
CCTV-1
9ee07411d0 Merge remote-tracking branch 'upstream/master' 2020-01-20 09:18:20 +08:00
klaxnek
a0e94f2f51 Mobile: AI Personality Translation 2020-01-19 16:09:47 +00:00
CCTV-1
be99725251 fix missing translation 2020-01-19 18:58:41 +08:00
Michael Kamensky
25e9b942b1 Merge branch 'master' into 'master'
Improve ProtectAi for cards that sacrifice themselves as a part of cost payment.

See merge request core-developers/forge!2453
2020-01-19 07:26:13 +00:00
Agetian
ac041a79f6 - Improve ProtectAi for cards that sacrifice themselves as a part of cost payment. 2020-01-19 10:18:01 +03:00
Michael Kamensky
c59d99b8ae Merge branch 'master' into 'master'
Preparing Forge for Android publish 1.6.32.001 [incremental].

See merge request core-developers/forge!2452
2020-01-19 05:22:28 +00:00
Agetian
3885e02d60 - Preparing Forge for Android publish 1.6.32.001 [incremental]. 2020-01-19 08:11:34 +03:00
CCTV-1
8b432cffd9 update translation 2020-01-19 10:04:25 +08:00
CCTV-1
51e5ce0e17 translate advanced search 2020-01-19 10:04:14 +08:00
CCTV-1
f2f984aff6 Merge remote-tracking branch 'upstream/master' 2020-01-19 10:03:19 +08:00
Blacksmith
a0d8a8a241 Clear out release files in preparation for next release 2020-01-19 00:59:24 +00:00
Blacksmith
b8101d0387 [maven-release-plugin] prepare for next development iteration 2020-01-19 00:54:23 +00:00
Blacksmith
55d02d5adb [maven-release-plugin] prepare release forge-1.6.32 2020-01-19 00:54:18 +00:00
Blacksmith
cf91b69d90 Update README.txt for release 2020-01-19 00:51:26 +00:00
Michael Kamensky
c2fdd86c29 Merge branch 'master' into 'master'
THB basic AI hints, quest precons, achievements, minor fixes + ELD quest precons

See merge request core-developers/forge!2451
2020-01-18 08:10:12 +00:00
Agetian
81af887694 - Enable THB on the Theros plane in Planar Conquest. 2020-01-18 10:47:52 +03:00
Agetian
85ef3aaa23 - Only 3 known booster pack images for ELD 2020-01-18 10:46:00 +03:00
Agetian
c8610da04b - Added quest precons for ELD and THB.
- Fixed a quest precon: Simic Guild Kit.
2020-01-18 10:27:47 +03:00
Agetian
490e204060 - Stylistic correction for an achievement. 2020-01-18 10:27:13 +03:00
Agetian
b2044944ff - THB achievements by Marek14. 2020-01-18 10:26:47 +03:00
Agetian
cbf7cc0b4c - Basic AI hints for THB. Minor card corrections. 2020-01-18 10:26:17 +03:00
CCTV-1
b9dbc14a4d Merge remote-tracking branch 'upstream/master' 2020-01-18 14:06:31 +08:00
CCTV-1
d1364066db update translation 2020-01-18 14:06:02 +08:00
CCTV-1
72d16f0636 translate some text 2020-01-18 14:05:40 +08:00
swordshine
3aa43b8246 Merge branch 'fix-translation' into 'master'
Fix missing translation

See merge request core-developers/forge!2449
2020-01-18 04:59:04 +00:00
Sol
ad288f5303 Merge branch 'thb17janb' into 'master'
THB fixes

See merge request core-developers/forge!2448
2020-01-18 04:21:49 +00:00
Tim Mocny
fcc96cf303 THB fixes 2020-01-18 04:21:48 +00:00
friarsol
ca1504b839 Fix missing translation 2020-01-17 23:20:39 -05:00
Sol
e265cb84cc Merge branch 'thb-formats' into 'master'
Add THB to rotating formats

See merge request core-developers/forge!2447
2020-01-18 03:01:27 +00:00
friarsol
6452189938 Add THB to rotating formats 2020-01-17 21:43:17 -05:00
Sol
3d7bd388e1 Merge branch 'patch-3' into 'master'
Update arena_trickster.txt

See merge request core-developers/forge!2446
2020-01-17 20:56:17 +00:00
Sol
21cfcac0c9 Merge branch 'thb17jan' into 'master'
THB 1/17

See merge request core-developers/forge!2445
2020-01-17 19:34:29 +00:00
Tim Mocny
5eae6acae0 THB 1/17 2020-01-17 19:34:28 +00:00
Sol
1c0cb3a885 Update arena_trickster.txt 2020-01-17 19:33:52 +00:00
Hans Mackowiak
b43fbcfabc Merge branch 'patch-3' into 'master'
Update dreamshaper_shaman.txt

See merge request core-developers/forge!2444
2020-01-17 14:27:07 +00:00
Sol
76146d3c6b Update dreamshaper_shaman.txt 2020-01-17 14:08:36 +00:00
Sol
641a443c82 Merge branch 'migrate-tbd' into 'master'
Migrating Theros Beyond Death

See merge request core-developers/forge!2443
2020-01-17 13:37:39 +00:00
tehdiplomat
e1027ce7ef Migrating Theros Beyond Death 2020-01-17 08:31:28 -05:00
Sol
46422a163b Merge branch 'patch-2' into 'master'
Update ANNOUNCEMENTS.txt

See merge request core-developers/forge!2442
2020-01-17 13:20:30 +00:00
Sol
556d8bfd48 Update ANNOUNCEMENTS.txt 2020-01-17 13:15:15 +00:00
swordshine
5b8e709271 Merge branch 'mana-duplication' into 'master'
Maybe fix Mana Reflection mana duplication

See merge request core-developers/forge!2430
2020-01-17 13:09:25 +00:00
Michael Kamensky
6fce5cc5a4 Merge branch 'patch011520' into 'master'
More Oracle updates, Essence of the Wild fix (issue #1234) and Once and Future QOL

Closes #1234

See merge request core-developers/forge!2439
2020-01-17 04:19:39 +00:00
Tim Mocny
b504b60112 More Oracle updates, Essence of the Wild fix (issue #1234) and Once and Future QOL 2020-01-17 04:19:39 +00:00
Michael Kamensky
c8b4e574b8 Merge branch 'thbdraft' into 'master'
Draft Rankings for THB and other settings to allow drafting

See merge request core-developers/forge!2441
2020-01-17 04:19:29 +00:00
austinio7116
32f117c7ec Added draft block, printsheet and booster details 2020-01-16 21:59:42 +00:00
maustin
95d3642d2e Merge branch 'coremaster' into thbdraft 2020-01-16 21:51:55 +00:00
austinio7116
c2ed631327 Merge branch 'thb15' into 'master'
THB 1/15 tweaks and such

See merge request core-developers/forge!2440
2020-01-16 21:49:58 +00:00
Tim Mocny
1f90f4e470 THB 1/15 tweaks and such 2020-01-16 21:49:58 +00:00
austinio7116
d397db9cfb Added draft rankings 2020-01-16 21:42:42 +00:00
swordshine
b5616362b0 Merge branch 'patch-6' into 'master'
Update es-ES.properties

See merge request core-developers/forge!2438
2020-01-16 01:14:41 +00:00
klaxnek
5d2a32c114 Update es-ES.properties 2020-01-15 15:38:43 +00:00
swordshine
5a64b6c297 Merge branch 'master' into 'master'
translate some text

See merge request core-developers/forge!2436
2020-01-15 13:42:36 +00:00
Michael Kamensky
095e720435 Merge branch 'patch-5' into 'master'
Update StaticAbilityContinuous: for Gain Abilities, only set OriginalHost if it wasn't already set

See merge request core-developers/forge!2437
2020-01-15 13:37:15 +00:00
Hans Mackowiak
6e4908acc3 Update StaticAbilityContinuous: for Gain Abilities, only set OriginalHost if it wasn't already set 2020-01-15 13:37:15 +00:00
CCTV-1
b053f79de3 update translation 2020-01-15 20:20:48 +08:00
CCTV-1
33564d023c translate some text 2020-01-15 20:20:25 +08:00
swordshine
57f445b791 Merge branch 'master' into 'master'
update translation

See merge request core-developers/forge!2433
2020-01-15 11:31:57 +00:00
swordshine
f79e3cad2a Merge branch 'patch-5' into 'master'
Update es-ES.properties

See merge request core-developers/forge!2434
2020-01-15 11:31:27 +00:00
swordshine
0d8a2d4a1d Merge branch 'patch' into 'master'
Add Heliod's Punishment

See merge request core-developers/forge!2435
2020-01-15 11:31:21 +00:00
swordshine
2df1770781 Fix a typo in the oracle text 2020-01-15 18:51:47 +08:00
klaxnek
51ec73bdf9 Update es-ES.properties 2020-01-15 10:42:05 +00:00
klaxnek
4299db4041 Update es-ES.properties 2020-01-15 10:41:09 +00:00
swordshine
c3a95a07a1 Add Heliod's Punishment 2020-01-15 18:39:46 +08:00
CCTV-1
38d614a567 update simplified chinese translation 2020-01-15 16:54:28 +08:00
CCTV-1
c0d77a7fdf fix translation key does not match 2020-01-15 16:53:54 +08:00
swordshine
806d62129b Merge branch 'patch' into 'master'
Add three cards

See merge request core-developers/forge!2432
2020-01-15 08:46:47 +00:00
swordshine
dffd0cd62c Add three cards 2020-01-15 16:33:43 +08:00
swordshine
333a96bb8d Merge branch 'patch' into 'master'
Add Hateful Eidolon

See merge request core-developers/forge!2431
2020-01-15 08:30:41 +00:00
swordshine
1aadc7991f Add Hateful Eidolon 2020-01-15 13:12:47 +08:00
swordshine
a41dc4bbd9 Merge branch 'master' into 'master'
translate some text

See merge request core-developers/forge!2422
2020-01-15 04:00:38 +00:00
friarsol
6529e02c83 Maybe fix Mana Reflection mana duplication 2020-01-14 21:12:47 -05:00
swordshine
476c2e4ce3 Merge branch 'patch' into 'master'
Fix The Birth of Meletis

See merge request core-developers/forge!2429
2020-01-15 02:08:41 +00:00
swordshine
706055f1b5 Fix The Birth of Meletis 2020-01-15 09:21:34 +08:00
swordshine
18c964ee3c Merge branch 'thb14' into 'master'
THB 1/14

See merge request core-developers/forge!2423
2020-01-15 00:48:35 +00:00
Tim Mocny
2a6ba11552 THB 1/14 2020-01-15 00:48:35 +00:00
swordshine
ef9fb6ea68 Merge branch 'temp' into 'master'
Hostage Taker wording

See merge request core-developers/forge!2427
2020-01-15 00:47:59 +00:00
swordshine
7055c11d16 Merge branch 'Jan14Oracle' into 'master'
January 14 Oracle Changes

See merge request core-developers/forge!2426
2020-01-15 00:47:28 +00:00
Tim Mocny
b0bc918bc1 January 14 Oracle Changes 2020-01-15 00:47:28 +00:00
Michael Kamensky
bc030355fc Merge branch 'master' into 'master'
Fix references in Sunbird's Invocation.

See merge request core-developers/forge!2428
2020-01-14 18:06:15 +00:00
Agetian
873c748e76 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2020-01-14 20:58:45 +03:00
Agetian
9a987ec7a3 - Fix references in Sunbird's Invocation. 2020-01-14 20:55:08 +03:00
Adam Pantel
b7d2790a8b Hostage Taker wording 2020-01-14 12:07:47 -05:00
swordshine
98c7b47681 Merge branch 'patch' into 'master'
Fix Hero of the Games

See merge request core-developers/forge!2425
2020-01-14 15:58:43 +00:00
swordshine
992c81263a Fix Hero of the Games 2020-01-14 23:53:21 +08:00
Hans Mackowiak
d9e9076fb4 Merge branch 'patch-5' into 'master'
Update Throne of Eldraine.txt

See merge request core-developers/forge!2424
2020-01-14 14:21:35 +00:00
Tim Mocny
938d5259a4 Update Throne of Eldraine.txt 2020-01-14 14:21:35 +00:00
CCTV-1
8d64694ac7 update translation 2020-01-14 13:19:20 +08:00
CCTV-1
1a55341dd3 translate some text 2020-01-14 13:18:43 +08:00
swordshine
3f2d70c315 Merge branch 'patch' into 'master'
Add more cards

See merge request core-developers/forge!2421
2020-01-14 01:54:48 +00:00
swordshine
fe005f3232 Add more cards 2020-01-14 09:46:49 +08:00
Hans Mackowiak
38f573bfce Merge branch 'patch' into 'master'
Add more THB cards

See merge request core-developers/forge!2417
2020-01-14 01:15:22 +00:00
swordshine
efc2d3a8e6 Merge branch 'thb13a' into 'master'
THB 1/13

See merge request core-developers/forge!2420
2020-01-14 01:13:09 +00:00
Tim Mocny
b2f0f0ca84 THB 1/13 2020-01-14 01:13:09 +00:00
swordshine
fd347c10d0 Merge branch 'savant' into 'master'
Arcane Savant and friends

See merge request core-developers/forge!2396
2020-01-14 01:12:09 +00:00
swordshine
0aacc3e2da Merge branch 'patch-5' into 'master'
Update rankle_master_of_pranks.txt

See merge request core-developers/forge!2418
2020-01-14 01:03:20 +00:00
Tim Mocny
fea69dfe06 Update rankle_master_of_pranks.txt 2020-01-14 01:03:20 +00:00
swordshine
e8e45cc378 Add Shadowspear 2020-01-13 23:02:49 +08:00
swordshine
5d6047ca7d Merge branch 'patch' into 'master'
Add more THB cards

See merge request core-developers/forge!2416
2020-01-13 14:37:37 +00:00
swordshine
6ad5e6d974 Add more THB cards 2020-01-13 14:37:37 +00:00
swordshine
82ced80a3e Merge branch 'patch' into 'master'
Add more THB cards

See merge request core-developers/forge!2414
2020-01-13 12:47:52 +00:00
swordshine
79347c0f9c Add more THB cards 2020-01-13 12:47:52 +00:00
swordshine
7b060320b5 Merge branch 'patch-4' into 'master'
Update es-ES.properties. Fixed some translations.

See merge request core-developers/forge!2413
2020-01-13 10:38:49 +00:00
klaxnek
38f4d4aa1b Update es-ES.properties. Fixed some translations. 2020-01-13 10:30:30 +00:00
swordshine
3dc80a8342 Merge branch 'master' into 'master'
translate some text

See merge request core-developers/forge!2412
2020-01-13 08:01:55 +00:00
swordshine
8dbfd9050c Merge branch 'thb10b' into 'master'
THB 1/10!

See merge request core-developers/forge!2406
2020-01-13 07:59:46 +00:00
Tim Mocny
c5604bddac THB 1/10! 2020-01-13 07:59:46 +00:00
CCTV-1
789401ed24 fix:some text translate 2020-01-13 13:44:16 +08:00
CCTV-1
c0d967a467 update translation 2020-01-13 13:34:14 +08:00
CCTV-1
75d49554da translate some text 2020-01-13 13:33:55 +08:00
swordshine
d228429b56 Merge branch 'stacktranslation01' into 'master'
StackTranslation01 - Translate PermanentEffect Card Name

See merge request core-developers/forge!2411
2020-01-13 00:44:14 +00:00
swordshine
9630d1a26b Merge branch 'spanishcardsfinal' into 'master'
Spanish Cards Translation (T..Z)

See merge request core-developers/forge!2410
2020-01-13 00:43:35 +00:00
klaxnek
2922f99ae3 Undo changes 2020-01-12 16:41:06 +01:00
klaxnek
cf7a7177c7 Translate PermanentCreatureEffect Card Name 2020-01-12 16:37:06 +01:00
klaxnek
f5dcbf4e6e Wrong commit 2020-01-12 15:02:03 +01:00
klaxnek
60c946a501 Spanish Cards Translation (T..Z). Fixed some untranslated cards. Finished!!! 2020-01-12 14:58:57 +01:00
Peter
28393d4571 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2020-01-12 14:46:55 +01:00
Michael Kamensky
eacfa96df4 Merge branch 'thb' into 'master'
Whirlwind Denial

See merge request core-developers/forge!2409
2020-01-12 04:40:30 +00:00
Michael Kamensky
576f83b5dc Merge branch 'master' into 'master'
Improve CopyPermanentAi for Bramble Sovereign.

Closes #1231

See merge request core-developers/forge!2408
2020-01-12 04:39:47 +00:00
Adam Pantel
b35ea79083 Whirlwind Denial 2020-01-11 18:20:30 -05:00
Agetian
45f2193e2a - NPE guard. 2020-01-11 16:44:44 +03:00
Agetian
b329786659 - Improve CopyPermanentAi for Bramble Sovereign. 2020-01-11 16:43:20 +03:00
Michael Kamensky
adf181aaec Merge branch 'patch-4' into 'master'
Update seven_dwarves.txt

See merge request core-developers/forge!2407
2020-01-11 04:16:10 +00:00
Sol
a95fff5ae3 Update seven_dwarves.txt 2020-01-11 03:25:58 +00:00
Adam Pantel
f401c3900c Arcane Savant and friends 2020-01-10 18:29:44 -05:00
Michael Kamensky
1f01e2ae16 Merge branch '1228-test-failure-testclonetransform' into 'master'
Resolve "Test failure: testCloneTransform"

Closes #1228

See merge request core-developers/forge!2404
2020-01-10 10:24:07 +00:00
Hans Mackowiak
d5cc3289dc Resolve "Test failure: testCloneTransform" 2020-01-10 10:24:07 +00:00
swordshine
9a0ba159b0 Merge branch 'master' into 'master'
Override getParamOrDefault for the sake of WrappedAbility, fixes Verdurous Gearhulk AI

Closes #1227

See merge request core-developers/forge!2403
2020-01-10 07:47:33 +00:00
Michael Kamensky
71dfe3146b Merge branch 'patch' into 'master'
Add Eidolon of Obstruction

See merge request core-developers/forge!2402
2020-01-10 07:31:00 +00:00
Agetian
9ed01ebbb6 - Override getParamOrDefault for the sake of WrappedAbility, fixes Verdurous Gearhulk AI allocation. 2020-01-10 10:27:30 +03:00
swordshine
0e7af09df3 Add Eidolon of Obstruction 2020-01-10 15:13:26 +08:00
swordshine
3c4844e46d Merge branch 'thb' into 'master'
Fix Dig in Thassas Oracle

See merge request core-developers/forge!2401
2020-01-10 01:11:48 +00:00
swordshine
965b67577a Merge branch 'thb9' into 'master'
THB 1/9

See merge request core-developers/forge!2399
2020-01-10 01:11:28 +00:00
Tim Mocny
8bcb38d4fc THB 1/9 2020-01-10 01:11:28 +00:00
Adam Pantel
071873718e Fix Dig in Thassas Oracle, disallow cancel 2020-01-09 14:16:39 -05:00
Michael Kamensky
7583b67bda Merge branch '1222-exception-with-dfcs-and-copy-effects' into 'master'
Resolve "Exception with DFCs and copy effects"

Closes #1222

See merge request core-developers/forge!2400
2020-01-09 15:32:20 +00:00
Hans Mackowiak
96d3ddf9b5 Resolve "Exception with DFCs and copy effects" 2020-01-09 15:32:20 +00:00
swordshine
dc0ddf542b Merge branch 'temp' into 'master'
Clear controller when leaving battlefield/stack

Closes #619

See merge request core-developers/forge!2397
2020-01-09 14:21:36 +00:00
swordshine
724e483ae3 Merge branch 'patch' into 'master'
Fix Nykthos

See merge request core-developers/forge!2398
2020-01-09 13:06:14 +00:00
swordshine
910fef3ecd Fix Nykthos 2020-01-09 20:59:20 +08:00
Adam Pantel
65288905d0 Clear controller when leaving battlefield/stack 2020-01-08 20:12:13 -05:00
swordshine
82d7171a92 Merge branch 'patch-5' into 'master'
THB 1/8

See merge request core-developers/forge!2395
2020-01-09 00:55:41 +00:00
Tim Mocny
20b51f59b0 Storm Herald - I didn't add AtEOT$ Exile to the animate anticipating some corner case Izzet shenanigans where this card gets flashed in on someone else's turns and the auras last until YOUR next end step. Possible it still can be cleaned up in some way. But this seems correct. 2020-01-09 00:55:41 +00:00
swordshine
16c06c7d7d Merge branch 'thb' into 'master'
Taranika, Thassas Oracle

See merge request core-developers/forge!2393
2020-01-09 00:55:03 +00:00
Adam Pantel
0aa20cb3f4 Taranika, Thassas Oracle 2020-01-08 10:08:59 -05:00
swordshine
71787b7059 Merge branch 'patch' into 'master'
Add Altar of the Pantheon

See merge request core-developers/forge!2392
2020-01-08 10:43:50 +00:00
Michael Kamensky
a9407467a5 Merge branch 'patch-escapeMod' into 'master'
CardFactoryUtil: add Escaped Modifier

See merge request core-developers/forge!2394
2020-01-08 10:18:56 +00:00
Hans Mackowiak
896eb79d2c CardFactoryUtil: add Escaped Modifier 2020-01-08 10:18:56 +00:00
swordshine
fb6f34d67e Merge branch 'spanishcards06' into 'master'
Spanish cards translation (N-O-P-Q-R-S)

See merge request core-developers/forge!2391
2020-01-08 07:33:08 +00:00
Blacksmith
9e9570d191 Clear out release files in preparation for next release 2020-01-08 02:25:33 +00:00
Blacksmith
1b130ab157 [maven-release-plugin] prepare for next development iteration 2020-01-08 02:20:36 +00:00
Blacksmith
74005529f5 [maven-release-plugin] prepare release forge-1.6.31 2020-01-08 02:20:31 +00:00
Blacksmith
4186a365cb Update README.txt for release 2020-01-08 02:18:23 +00:00
swordshine
4366db1acc Add Altar of the Pantheon 2020-01-08 08:42:28 +08:00
klaxnek
4679e00a9e Spanish cards translation (N-O-P-Q-R-S) 2020-01-07 17:32:06 +01:00
Michael Kamensky
51960f0e28 Merge branch 'temp' into 'master'
DigUntil should do nothing on 0

See merge request core-developers/forge!2390
2020-01-07 16:29:43 +00:00
Adam Pantel
a27f69b1c5 DigUntil does nothing on 0 2020-01-07 10:06:33 -05:00
Michael Kamensky
8ab01cce3e Merge branch 'patch' into 'master'
Update Pharika's Spawn using ImmediateTrigger

See merge request core-developers/forge!2388
2020-01-07 11:54:20 +00:00
swordshine
e10235aae9 Remove a cleanup for Atris 2020-01-07 16:48:27 +08:00
swordshine
05591c6bd9 Atris should use targeting 2020-01-07 16:46:16 +08:00
swordshine
a25e469291 Simplify the script of Aphemia, the Cacophony 2020-01-07 14:41:50 +08:00
swordshine
ebd14944c6 Update Pharika's Spawn using ImmediateTrigger 2020-01-07 14:35:23 +08:00
swordshine
1b08fc3592 Merge branch 'patch-5' into 'master'
THB 1/6

See merge request core-developers/forge!2384
2020-01-07 06:29:45 +00:00
Tim Mocny
57457946e5 THB 1/6 2020-01-07 06:29:45 +00:00
Michael Kamensky
918a39ab60 Merge branch 'prerelease-standardization' into 'master'
Prerelease standardization

Closes #520

See merge request core-developers/forge!2379
2020-01-07 05:59:26 +00:00
Sol
2cc39c2580 Prerelease standardization 2020-01-07 05:59:26 +00:00
swordshine
3aca94dc9b Merge branch 'temp' into 'master'
Kiora, Uro

See merge request core-developers/forge!2385
2020-01-07 05:52:40 +00:00
apantel
65ad9d61d9 Kiora, Uro 2020-01-06 23:41:40 -05:00
swordshine
138e3a4661 Merge branch 'bug-fixes' into 'master'
Extracting two bugfixes from Lerio's MR

Closes #1211

See merge request core-developers/forge!2387
2020-01-07 04:12:37 +00:00
Sol
41844ee7e3 Extracting two bugfixes from Lerio's MR 2020-01-07 04:12:37 +00:00
Sol
d4f2594e26 Merge branch 'patch-4' into 'master'
ghireds_belligerence fix damage

See merge request core-developers/forge!2386
2020-01-07 02:02:54 +00:00
Hans Mackowiak
babc9de575 ghireds_belligerence fix damage 2020-01-07 02:02:54 +00:00
Michael Kamensky
11a5683025 Merge branch 'patch-3' into 'master'
Update missing translations in es-ES.properties

See merge request core-developers/forge!2383
2020-01-06 14:42:21 +00:00
Michael Kamensky
b6f8f89c7a Merge branch 'spanishcards05' into 'master'
Spanish cards translation (L and M)

See merge request core-developers/forge!2382
2020-01-06 14:42:16 +00:00
klaxnek
0885fe5898 Update es-ES.properties 2020-01-06 14:14:15 +00:00
klaxnek
8aa1f049ab Update missing translations in es-ES.properties 2020-01-06 11:44:58 +00:00
Peter
f7aa7bf580 Spanish cards translation (L and M) 2020-01-06 11:43:01 +01:00
Michael Kamensky
b74033fe17 Merge branch 'temp' into 'master'
Unneeded SubAbility on Primordial Mist

See merge request core-developers/forge!2380
2020-01-06 06:35:34 +00:00
Michael Kamensky
596296cabf Merge branch 'dashAltFix' into 'master'
Dash: fix keyword after AlternativeCost

See merge request core-developers/forge!2381
2020-01-06 06:35:15 +00:00
Hans Mackowiak
adcd86e7d7 Dash: fix keyword after AlternativeCost 2020-01-06 06:35:14 +00:00
Adam Pantel
308768ae50 Unneeded SubAbility on Primordial Mist 2020-01-05 23:42:31 -05:00
swordshine
235636ca4a Merge branch 'patch' into 'master'
Update more scripts

See merge request core-developers/forge!2376
2020-01-05 08:13:41 +00:00
swordshine
359ce5e39d Update more scripts 2020-01-05 15:54:15 +08:00
swordshine
c6e13e98b3 Merge branch 'patch-9' into 'master'
THB 1/4

See merge request core-developers/forge!2375
2020-01-05 07:52:40 +00:00
Tim Mocny
0cc9687e39 THB 1/4 2020-01-05 07:52:39 +00:00
swordshine
3d59f1832c Allure of the Unknown should reveal first 2020-01-05 14:09:33 +08:00
swordshine
58fdd58e9a Update scripts 2020-01-05 13:29:50 +08:00
swordshine
ea661c4fbb Merge branch 'translation' into 'master'
Keep translation files in sync

See merge request core-developers/forge!2374
2020-01-05 03:08:51 +00:00
Dagin
28abe52106 Keep translation files in sync 2020-01-04 20:23:05 +01:00
swordshine
e689fd8721 Merge branch 'master' into 'master'
fix card name translation

See merge request core-developers/forge!2373
2020-01-04 15:51:00 +00:00
CCTV-1
fea6c85e97 Merge remote-tracking branch 'upstream/master' 2020-01-04 22:25:17 +08:00
CCTV-1
4e80a1b818 get correct localization card name 2020-01-04 22:23:20 +08:00
Michael Kamensky
bdde3520f4 Merge branch 'patch' into 'master'
Update Haktos the Unscarred

See merge request core-developers/forge!2371
2020-01-04 10:21:08 +00:00
swordshine
165ae649b1 Merge branch 'temp' into 'master'
Fix more localization mismatches

See merge request core-developers/forge!2372
2020-01-04 05:57:59 +00:00
Adam Pantel
0536ebf1c8 Fix more localization mismatches 2020-01-03 23:21:13 -05:00
swordshine
fb176abe05 Update Haktos the Unscarred 2020-01-04 12:08:52 +08:00
swordshine
28f0232736 Merge branch 'temp' into 'master'
Mismatched localization labels

See merge request core-developers/forge!2370
2020-01-04 01:18:29 +00:00
swordshine
4c29fd8faf Merge branch 'patch-8' into 'master'
Update the_royal_scions.txt

See merge request core-developers/forge!2368
2020-01-04 01:17:32 +00:00
swordshine
df8d289ee0 Merge branch 'patch-7' into 'master'
Update synthetic_destiny.txt

See merge request core-developers/forge!2367
2020-01-04 01:17:03 +00:00
Tim Mocny
203de48dd4 Update synthetic_destiny.txt 2020-01-04 01:17:03 +00:00
swordshine
97f763b1a0 Merge branch 'patch-6' into 'master'
THB 12/31

See merge request core-developers/forge!2360
2020-01-04 01:15:59 +00:00
Tim Mocny
9fd8969ea5 THB 12/31 2020-01-04 01:15:58 +00:00
Adam Pantel
d70e699c81 Mismatched localization labels 2020-01-03 16:06:01 -05:00
Tim Mocny
39bf8f5313 Update the_royal_scions.txt 2020-01-03 14:58:57 +00:00
swordshine
68eb8e2510 Merge branch 'master' into 'master'
use localization card name in translation text

See merge request core-developers/forge!2357
2020-01-03 13:32:01 +00:00
Michael Kamensky
a20800cfb3 Merge branch 'patch-2' into 'master'
Update es-ES.properties

See merge request core-developers/forge!2366
2020-01-03 13:11:02 +00:00
Michael Kamensky
adce811a3d Merge branch 'devotion-choosecolor' into 'master'
Added a simple logic for ChooseColor based on devotion (Nyx Lotus)

See merge request core-developers/forge!2365
2020-01-03 13:09:27 +00:00
Michael Kamensky
084a7ae73e Added a simple logic for ChooseColor based on devotion (Nyx Lotus) 2020-01-03 13:09:27 +00:00
klaxnek
14a697269b Update es-ES.properties 2020-01-03 11:35:56 +00:00
swordshine
7de8cdee90 Merge branch 'german-translation' into 'master'
Update de-DE.properties from forum

See merge request core-developers/forge!2355
2020-01-03 06:50:43 +00:00
swordshine
20333491f7 Merge branch 'patch-2' into 'master'
it-IT.properties from forum

See merge request core-developers/forge!2363
2020-01-03 06:50:34 +00:00
swordshine
d529c55277 Merge branch 'patch-3' into 'master'
cardnames-it-IT.txt from forum

See merge request core-developers/forge!2364
2020-01-03 06:50:26 +00:00
CCTV-1
9e3bb1ca72 use localization card name 2020-01-03 13:43:17 +08:00
CCTV-1
0caf41f1d6 convert indentation to spaces 2020-01-03 13:33:59 +08:00
CCTV-1
817aadb995 Merge remote-tracking branch 'upstream/master' 2020-01-03 13:32:15 +08:00
Michael Kamensky
c6016c2728 Merge branch 'patch-5' into 'master'
Update visage_of_bolas.txt (typo in line 5)

See merge request core-developers/forge!2359
2020-01-03 04:40:50 +00:00
Tim Mocny
492cab9c06 Update visage_of_bolas.txt (typo in line 5) 2020-01-03 04:40:50 +00:00
Michael Kamensky
64dd84b560 Merge branch 'lbl-error' into 'master'
Fix lblPlayerActivatedCardChooseMode reference

See merge request core-developers/forge!2362
2020-01-03 04:40:30 +00:00
Michael Kamensky
496768b566 Merge branch 'escapeRefactorFlashback' into 'master'
Refactor Escape and AlternativeCost

See merge request core-developers/forge!2361
2020-01-03 04:40:10 +00:00
Hans Mackowiak
b2fcc6e743 Refactor Escape and AlternativeCost 2020-01-03 04:40:09 +00:00
Churrufli
1584fcbb82 cardnames-it-IT.txt 2020-01-02 17:30:29 +00:00
Churrufli
2860111154 it-IT.properties 2020-01-02 17:21:24 +00:00
Dagin Svezek
cfdbb98cf9 Merge branch 'master' into 'german-translation'
# Conflicts:
#   forge-gui/res/languages/de-DE.properties
2020-01-02 12:23:37 +00:00
Dagin Svezek
87ec3a528b Update de-DE.properties from forum 2020-01-02 12:15:17 +00:00
CCTV-1
8ebabd978f add cast check 2020-01-02 12:17:38 +08:00
CCTV-1
51fae70e24 Merge remote-tracking branch 'upstream/master' 2020-01-02 11:51:07 +08:00
CCTV-1
2f0024a8dd rollback unsafe changes 2020-01-02 11:45:38 +08:00
Adam Pantel
cd498e408c Fix lblPlayerActivatedCardChooseMode reference 2020-01-01 21:31:06 -05:00
swordshine
6d193c315e Merge branch 'vs-null' into 'master'
Add null check for Volrath's Shapeshifter

See merge request core-developers/forge!2358
2020-01-01 03:22:26 +00:00
Adam Pantel
c81d9f6bc2 Add null check for Volrath's Shapeshifter 2019-12-31 17:05:29 -05:00
CCTV-1
e13e5d0b16 'reduce the number of words' 2019-12-31 21:37:05 +08:00
CCTV-1
ceb892d610 use localization card name in translation text 2019-12-31 21:16:06 +08:00
swordshine
2690a4d548 Merge branch 'master' into 'master'
update translation text

See merge request core-developers/forge!2351
2019-12-31 06:00:21 +00:00
swordshine
59a8e550d6 Merge branch 'my-branch' into 'master'
3rd try is the charm

See merge request core-developers/forge!2350
2019-12-31 05:59:38 +00:00
CCTV-1
4793425c58 translate ZoneType 2019-12-30 19:08:57 +08:00
CCTV-1
e47ee567bf update translation 2019-12-30 10:35:38 +08:00
CCTV-1
c9f33415e1 translate enum ZoneType 2019-12-30 10:35:16 +08:00
Sol
d1a0c9f1ff Merge branch 'updatedeldstandard' into 'master'
Updated pioneer deckgen data with lots more up-to-date meta data

See merge request core-developers/forge!2353
2019-12-30 01:25:02 +00:00
austinio7116
40b43b00c2 Updated pioneer deckgen data with lots more up-to-date meta data
(cherry picked from commit f490497)

(cherry picked from commit 729dfea)
2019-12-30 01:25:02 +00:00
CCTV-1
cea6d3a9df update simplified chinese translation 2019-12-30 08:48:07 +08:00
CCTV-1
ecda5377a3 Merge remote-tracking branch 'upstream/master' 2019-12-30 08:47:37 +08:00
Northmoc
fb9c7290b2 Tymaret Calls the Dead 2019-12-29 14:05:13 -05:00
Northmoc
5b185d2fe3 fixing Gallia 2019-12-29 12:17:23 -05:00
Northmoc
c4712e705c Gallia the satyr bear :) 2019-12-29 08:31:30 -05:00
Northmoc
d4656b619f Merge branch 'master' of https://git.cardforge.org/core-developers/forge into my-branch 2019-12-29 08:06:37 -05:00
Northmoc
e8c5a58d85 Allure of the Unknown 2019-12-29 08:06:17 -05:00
Dagin Svezek
143cfa04de Update de-DE.properties from forum 2019-12-29 09:45:42 +00:00
austinio7116
ef752c5db6 Pioneer bannings up to Dec 2 2019 2019-12-28 19:23:30 +00:00
CCTV-1
f4ba889c84 update translation 2019-12-28 20:07:03 +08:00
CCTV-1
f20e61520b update translation text to use have placeholder string 2019-12-28 20:06:51 +08:00
CCTV-1
88f082e261 Merge remote-tracking branch 'upstream/master' 2019-12-28 11:18:26 +08:00
Northmoc
6304fa4091 Merge branch 'master' of https://git.cardforge.org/core-developers/forge into my-branch 2019-12-27 18:59:39 -05:00
Northmoc
4b01074d0f Edition update 2019-12-27 09:02:38 -05:00
austinio7116
65c8e47e26 Updated standard deckgen data with lots more up-to-date meta data
(cherry picked from commit f490497)
2019-12-27 14:00:57 +00:00
Northmoc
80c6e30691 The Binding of the Titans 2019-12-27 08:59:54 -05:00
CCTV-1
8cfc922d97 update translation 2019-12-27 15:22:52 +08:00
CCTV-1
92e6c303ad update translation text to use have placeholder string 2019-12-27 15:22:01 +08:00
Northmoc
762dc60600 TBD - card typo fix and card list update 2019-12-26 10:07:18 -05:00
Northmoc
373b81072a Merge branch 'my-branch' of https://git.cardforge.org/Northmoc/forge into my-branch 2019-12-26 09:00:39 -05:00
Northmoc
9bf2d57715 Fix for issue #1219 2019-12-26 13:58:18 +00:00
Northmoc
ef71a7befc Contributor :) 2019-12-26 13:58:18 +00:00
Northmoc
dc34c4e985 Last THB leaks/spoilers 2019-12-26 13:58:18 +00:00
Northmoc
9a495e516a Few more THB 2019-12-26 13:58:18 +00:00
Northmoc
6fcd744132 The Akroan War 2019-12-26 13:58:18 +00:00
Northmoc
70bc280cf1 4 more THB cards 12/20 2019-12-26 13:58:18 +00:00
Northmoc
470fe4b877 Chainweb aracnir - escaping with counters 2019-12-26 13:58:18 +00:00
Northmoc
86f472d29f Chainweb aracnir - escaping with counters 2019-12-26 13:58:18 +00:00
Northmoc
b54ce90df1 S THB cards and some edits 2019-12-26 13:58:18 +00:00
Northmoc
db270d0c7c 3 THB Theme Booster rares 2019-12-26 13:58:18 +00:00
Northmoc
9d5837bb87 3 THB Theme Booster rares 2019-12-26 13:58:18 +00:00
Northmoc
12848af745 3 THB Omens 2019-12-26 13:58:18 +00:00
Northmoc
2d28139ca0 7 THB N creatures (mostly vanilla) 2019-12-26 13:58:18 +00:00
Northmoc
272663dc4a Fixing apostrophes in THB list 2019-12-26 13:58:18 +00:00
Northmoc
d4a47260cb Another round of THB 2019-12-26 13:58:18 +00:00
Northmoc
7023521fb2 Another round of THB 2019-12-26 13:58:18 +00:00
Northmoc
97fe8dc35b Fixing "you may search your library and/or your graveyard" cards so your library isn't auto-searched (and shuffled) 2019-12-26 13:58:18 +00:00
CCTV-1
fb0c0bd807 fix typo 2019-12-26 08:54:20 -05:00
CCTV-1
2285541b6d fix missing translate 2019-12-26 08:54:19 -05:00
CCTV-1
e965b899c0 update translation 2019-12-26 08:54:19 -05:00
CCTV-1
011811d183 translate mobile AssignDamage view 2019-12-26 08:54:19 -05:00
CCTV-1
65efbc57f3 translate dev menu 2019-12-26 08:54:19 -05:00
CCTV-1
437587766b translate pay mana message 2019-12-26 08:54:19 -05:00
CCTV-1
a1fe5c5db1 update interactive message translate 2019-12-26 08:54:19 -05:00
CCTV-1
c44d91281e translate ability/effects all interactive message(in Android because Chinese word wrap doesn't work,do not translate StackDescription for now) 2019-12-26 08:54:19 -05:00
CCTV-1
56fdf15b6f update translation 2019-12-26 08:54:19 -05:00
CCTV-1
19be99d106 translate all confirmPayment message argument 2019-12-26 08:54:19 -05:00
CCTV-1
2e25c83775 translate desktop AddBasicLandDialog.java 2019-12-26 08:54:19 -05:00
CCTV-1
dae91de27e translate "Select Order" 2019-12-26 08:54:19 -05:00
CCTV-1
15424d2fea translate "OK","No" 2019-12-26 08:54:18 -05:00
CCTV-1
cd1d744e1f fix typo 2019-12-26 08:25:06 +08:00
CCTV-1
dc77591571 Merge remote-tracking branch 'upstream/master' 2019-12-26 08:16:43 +08:00
Northmoc
074467e65e Fix for issue #1219 2019-12-25 21:04:50 +00:00
Northmoc
c81f93551e Contributor :) 2019-12-25 21:04:50 +00:00
Northmoc
1f6cbd7d19 Last THB leaks/spoilers 2019-12-25 21:04:50 +00:00
Northmoc
bd35e224a4 Few more THB 2019-12-25 21:04:50 +00:00
Northmoc
ae5986f101 The Akroan War 2019-12-25 21:04:50 +00:00
Northmoc
5cbf5649c0 4 more THB cards 12/20 2019-12-25 21:04:50 +00:00
Northmoc
3da4db3edc Chainweb aracnir - escaping with counters 2019-12-25 21:04:50 +00:00
Northmoc
47121019d4 Chainweb aracnir - escaping with counters 2019-12-25 21:04:50 +00:00
Northmoc
c97b390161 S THB cards and some edits 2019-12-25 21:04:50 +00:00
Northmoc
f97312dcf2 3 THB Theme Booster rares 2019-12-25 21:04:50 +00:00
Northmoc
0eed744018 3 THB Theme Booster rares 2019-12-25 21:04:50 +00:00
Northmoc
655737dcb5 3 THB Omens 2019-12-25 21:04:50 +00:00
Northmoc
488fa4a4dd 7 THB N creatures (mostly vanilla) 2019-12-25 21:04:50 +00:00
Northmoc
8344522e12 Fixing apostrophes in THB list 2019-12-25 21:04:50 +00:00
Northmoc
e1e085d610 Another round of THB 2019-12-25 21:04:50 +00:00
Northmoc
e35904e727 Another round of THB 2019-12-25 21:04:50 +00:00
Northmoc
6dc8d7648a Fixing "you may search your library and/or your graveyard" cards so your library isn't auto-searched (and shuffled) 2019-12-25 21:04:49 +00:00
Agetian
c296b32209 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-12-25 19:36:53 +03:00
Agetian
69f2bb70e8 - Added puzzle PS_ELDS (Superfriends Winter Vacation Puzzle) 2019-12-25 19:35:21 +03:00
Hans Mackowiak
a499e0a551 Combat: copyLastState after damage is applied 2019-12-25 17:25:32 +01:00
Michael Kamensky
bc99962621 Fix a hang in Card.java (e.g. Approach of the Second Sun) 2019-12-25 16:01:26 +00:00
Agetian
bf9b7054b3 - Update the fix. 2019-12-25 18:56:14 +03:00
Agetian
50be973e57 - Fix a hang in Card.java (e.g. Approach of the Second Sun) 2019-12-25 15:34:46 +03:00
CCTV-1
108c39ef7c fix missing translate 2019-12-25 18:42:35 +08:00
CCTV-1
ae6ea42aa9 update translation 2019-12-25 14:07:59 +08:00
CCTV-1
6c4ceccc30 translate mobile AssignDamage view 2019-12-25 14:07:28 +08:00
CCTV-1
fde7d6ff09 translate dev menu 2019-12-25 14:06:59 +08:00
CCTV-1
1af3505d0b translate pay mana message 2019-12-25 14:06:35 +08:00
CCTV-1
047f018da4 update interactive message translate 2019-12-24 11:42:46 +08:00
CCTV-1
a90e9f05e2 translate ability/effects all interactive message(in Android because Chinese word wrap doesn't work,do not translate StackDescription for now) 2019-12-23 22:53:38 +08:00
CCTV-1
7db7e10934 Merge remote-tracking branch 'upstream/master' 2019-12-23 22:39:55 +08:00
Hans Mackowiak
397b340701 Escape: fix Crash when outside of Game 2019-12-21 08:25:42 +00:00
CCTV-1
a050b584ac Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-12-21 12:38:06 +08:00
Myrd
d68c740e1d Use FileSection.parseToMap() from TriggerHandler. 2019-12-20 22:18:00 +00:00
Myrd
b807ce30c4 Merge branch 'dump_state_with_owner' into 'master'
Fix dumping state with cards with non-matching owner/controllers.

See merge request core-developers/forge!2342
2019-12-20 17:56:37 +00:00
Myrd
a4bd13d050 Fix dumping state with cards with non-matching owner/controllers. 2019-12-20 17:56:37 +00:00
Myrd
244960d0d7 Merge branch 'avoid_combat_sim' into 'master'
Avoid combat simulation if the current player has no creatures in play.

See merge request core-developers/forge!2341
2019-12-20 16:57:14 +00:00
Myrd
267c8c6b0d Avoid combat simulation if the current player has no creatures in play. 2019-12-20 16:57:14 +00:00
Myrd
06fc409234 Merge branch 'optimize_param_parsing' into 'master'
Optimize FileSection.parse()/parseMap().

See merge request core-developers/forge!2340
2019-12-20 16:37:31 +00:00
Alexei Svitkine
77ed24a203 Revert changes to GameCopier. 2019-12-20 11:28:11 -05:00
Alexei Svitkine
ed3c519404 Remove game copier logging code. 2019-12-20 11:25:43 -05:00
Myrd
9fcf8e82ef Merge branch 'reland_opts' into 'master'
Optimize some code paths in Forge (card type logic and property get with default).

See merge request core-developers/forge!2339
2019-12-20 16:24:11 +00:00
Alexei Svitkine
dd4df9baaa Optimize FileSection.parse()/parseMap().
This was showing up in profiles with simulation AI. The change
makes constants for the patterns used, so they don't have to be
"compiled" each time and also introduces a cache for these.

With this change, a GameCopier operation is sped up by about 30%
from my local measurement (I tried with a modern deck I have).
2019-12-20 11:22:06 -05:00
Alexei Svitkine
4384b1621b Switch card types lists to sets to avoid costly look ups and
optimize getParamOrDefault() to do a single look up, instead of
two. These were showing up in profiles when using simulation AI.
2019-12-20 11:15:38 -05:00
Myrd
0e1c82a31f Merge branch 'revert-b7c47200' into 'master'
Revert "Merge branch 'master' into 'master'"

See merge request core-developers/forge!2338
2019-12-20 15:53:35 +00:00
Hans Mackowiak
b5babd5451 Merge branch 'vanguard-missing-ability-text' into 'master'
Fix Vanguards etc not displaying abilities

See merge request core-developers/forge!2336
2019-12-20 15:52:18 +00:00
Myrd
5cb8502566 Revert "Merge branch 'master' into 'master'"
This reverts merge request !2334
2019-12-20 15:48:04 +00:00
Myrd
b7c4720030 Merge branch 'master' into 'master'
Optimize some code paths in Forge (card type logic and property get with default).

See merge request core-developers/forge!2334
2019-12-20 15:47:44 +00:00
Michael Kamensky
074243891c Merge branch 'sort_flip_results' into 'master'
Sort coing flips for Krark's Thumb

See merge request core-developers/forge!2335
2019-12-20 05:03:43 +00:00
CCTV-1
1117ff1e7c update translation 2019-12-20 12:36:53 +08:00
CCTV-1
5dd24341e1 translate all confirmPayment message argument 2019-12-20 12:36:30 +08:00
CCTV-1
19d4f36faa translate desktop AddBasicLandDialog.java 2019-12-20 12:34:28 +08:00
CCTV-1
ac9f8e3191 translate "Select Order" 2019-12-20 12:31:32 +08:00
CCTV-1
9d193fb3bb translate "OK","No" 2019-12-20 12:30:18 +08:00
friarsol
c20b0c3620 Fix Vanguards etc not displaying abilities 2019-12-19 23:23:39 -05:00
friarsol
8409a7e6d1 Sort coing flips for Krark's Thumb 2019-12-19 22:43:33 -05:00
Alexei Svitkine
3d59118b94 Use Sets instead of Lists for card types to avoid slow lookups. 2019-12-19 20:49:29 -05:00
Alexei Svitkine
e0fb373b3d Use Java 8's getOrDefault() 2019-12-19 20:48:01 -05:00
swordshine
f0c8deb25e Merge branch 'master' into 'master'
some translate fix

See merge request core-developers/forge!2332
2019-12-19 01:16:42 +00:00
Michael Kamensky
b288c9f61a Merge branch 'Hanmac-master-patch-98814' into 'master'
Spell:checkOtherRestrictions check CantBeCast for changed Cardface

Closes #1198

See merge request core-developers/forge!2333
2019-12-18 11:57:26 +00:00
Hans Mackowiak
823c278cd7 Spell:checkOtherRestrictions check CantBeCast for changed Cardface 2019-12-18 11:57:26 +00:00
Michael Kamensky
ba4ba8d051 Merge branch 'keyword-escape' into 'master'
Keyword: add Escape Mechanic

Closes #1199

See merge request core-developers/forge!2331
2019-12-18 10:38:02 +00:00
Hans Mackowiak
467268c451 Keyword: add Escape Mechanic 2019-12-18 10:38:02 +00:00
CCTV-1
3f1f8321d3 do not translate "for {CR}" 2019-12-18 18:04:50 +08:00
CCTV-1
5730a407c3 Merge remote-tracking branch 'upstream/master' 2019-12-18 17:56:47 +08:00
CCTV-1
22974f8bf4 fix typo 2019-12-18 17:52:29 +08:00
CCTV-1
6414347d91 fix:wrong comment 2019-12-18 17:51:59 +08:00
swordshine
113f891348 Merge branch 'master' into 'master'
translate some text

See merge request core-developers/forge!2328
2019-12-18 09:19:40 +00:00
CCTV-1
cbab3e6000 LoadQuestScreen.java "New" should not be use lblNew 2019-12-18 13:21:32 +08:00
CCTV-1
17663bd2c0 translate mobile quest 2019-12-18 12:56:41 +08:00
CCTV-1
c226a4dbd5 Merge remote-tracking branch 'upstream/master' 2019-12-18 12:44:02 +08:00
Michael Kamensky
0c2d0e6cb6 Merge branch 'puzzle-improvements' into 'master'
Puzzle improvements

See merge request core-developers/forge!2330
2019-12-18 04:30:45 +00:00
friarsol
d63106a71a Save completed puzzles, and sort them to the bottom of the screen 2019-12-17 21:16:17 -05:00
Michael Kamensky
2c26e3f509 Merge branch 'master' into 'master'
Added puzzle PS_ELD9 - Possibility Storm - Throne of Eldraine 09

See merge request core-developers/forge!2329
2019-12-17 14:55:42 +00:00
Agetian
50e0387cda - Fix AI persistent mana in PS_ELD9. 2019-12-17 17:52:17 +03:00
Agetian
b05dc40a1c - Added puzzle PS_ELD9. 2019-12-17 17:50:25 +03:00
CCTV-1
3846be2321 translate some text 2019-12-17 19:49:11 +08:00
swordshine
2fa3179a31 Merge branch 'master' into 'master'
translate InputAttack.java and InputBlock.java

See merge request core-developers/forge!2326
2019-12-17 01:30:07 +00:00
CCTV-1
2617ff339d translate InputAttack.java and InputBlock.java 2019-12-16 20:03:28 +08:00
swordshine
e36311db44 Merge branch 'master' into 'master'
More upcoming THB cards

See merge request core-developers/forge!2324
2019-12-16 07:38:06 +00:00
Michael Kamensky
2449166c36 Merge branch 'patch-6' into 'master'
Update enchanted_carriage: fixed trigger

See merge request core-developers/forge!2325
2019-12-15 06:01:35 +00:00
Hans Mackowiak
eb16486ee6 Update enchanted_carriage: fixed trigger 2019-12-15 06:01:35 +00:00
friarsol
e0c858a3d6 Only split the first colon from Puzzle metadata 2019-12-14 22:21:23 -05:00
Northmoc
71749cef3f More upcoming THB 2019-12-14 16:47:25 -05:00
Northmoc
15839e343e More upcoming THB 2019-12-14 12:50:24 -05:00
Northmoc
9a98b39e94 More upcoming THB 2019-12-13 13:58:46 -05:00
Northmoc
9c6c0f1861 More upcoming THB 2019-12-13 12:24:47 -05:00
Northmoc
318d919111 More upcoming THB 2019-12-12 14:03:12 -05:00
Northmoc
c753a1c875 Merge branch 'master' of https://git.cardforge.org/core-developers/forge
 Conflicts:
	forge-gui/res/cardsfolder/upcoming/moss_viper.txt
2019-12-11 10:57:43 -05:00
Northmoc
e304337a62 More upcoming THB 2019-12-11 10:33:10 -05:00
Michael Kamensky
3768ca8f77 Merge branch 'master' into 'master'
Added puzzle PS_ELD8 - Possibility Storm - Throne of Eldraine 08

See merge request core-developers/forge!2322
2019-12-10 17:26:30 +00:00
Agetian
12f76827e9 - Added puzzle PS_ELD8. 2019-12-10 19:51:41 +03:00
Michael Kamensky
3c5f464039 Merge branch 'master' into 'master'
Set ExiledWith for the LKI copies (fixes Colfenor's Plans)

Closes #1215

See merge request core-developers/forge!2321
2019-12-10 16:43:41 +00:00
Agetian
9f2304b1a5 - Set ExiledWith for the LKI copies (fixes Colfenor's Plans) 2019-12-10 19:35:10 +03:00
swordshine
8a0ba4f27a Merge branch 'patch-5' into 'master'
Update iroas_god_of_victory.txt

See merge request core-developers/forge!2320
2019-12-09 03:55:24 +00:00
Sol
43dc6d35c0 Update iroas_god_of_victory.txt 2019-12-09 03:49:50 +00:00
swordshine
d04c593adc Merge branch 'patch' into 'master'
Update scripts

See merge request core-developers/forge!2319
2019-12-07 10:10:53 +00:00
swordshine
4e1b7e886f Update scripts 2019-12-07 17:52:59 +08:00
Michael Kamensky
8544e805bb Merge branch 'master' into 'master'
More THB: Funeral Rites

See merge request core-developers/forge!2318
2019-12-07 04:34:06 +00:00
klaxnek
7e18cbca97 Undo changes 2019-12-06 20:12:05 +01:00
klaxnek
f5734bb626 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-12-06 18:29:31 +01:00
Northmoc
08823bd94c Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-12-06 09:24:35 -05:00
Northmoc
04cfc7a81f Furious Rise, some "heroics" and a token (THB leaks) 2019-12-06 09:20:42 -05:00
Northmoc
91656fd7c0 Furious Rise, some "heroics" and a token (THB leaks) 2019-12-06 09:16:17 -05:00
Northmoc
e984bd5708 Funeral Rites 2019-12-05 20:05:45 -05:00
swordshine
eb0c0f3f84 Merge branch 'master' into 'master'
More THB cards and a relevant token

See merge request core-developers/forge!2316
2019-12-05 01:28:05 +00:00
klaxnek
acac0ee5e2 Add personal changes 2019-12-04 23:09:03 +01:00
Michael Kamensky
19b7daeaee Merge branch 'master' into 'master'
Added puzzle PS_ELD7 - Possibility Storm - Throne of Eldraine 07

See merge request core-developers/forge!2317
2019-12-04 16:55:30 +00:00
Agetian
c933e722f3 - Added puzzle PS_ELD7. 2019-12-04 19:03:54 +03:00
Northmoc
1536377eb2 Alirios, Enraptured -putting his token in the right spot 2019-12-04 10:27:38 -05:00
Northmoc
a11640cbc9 Alirios, Enraptured and his token 2019-12-03 23:47:03 -05:00
Northmoc
d51fe454b5 Eidolon of Philosophy and Flicker of Fate THB leaks 2019-12-03 19:36:27 -05:00
Michael Kamensky
550fd937f1 Merge branch 'master' into 'master'
THB leaked cards (in upcoming folder!)

See merge request core-developers/forge!2315
2019-12-03 18:57:41 +00:00
Northmoc
bd1add4d47 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-12-03 13:34:02 -05:00
Tim Mocny
944eb38002 dreamstalker_manticore.txt THB leak 2019-12-03 12:31:07 -05:00
Tim Mocny
ddf481f4fd THB leak (upcoming) 2019-12-03 11:35:58 -05:00
Tim Mocny
2e7d84b58f Upload New File 2019-12-03 14:13:31 +00:00
Tim Mocny
db853cb5ba Upload New File 2019-12-03 14:12:46 +00:00
Tim Mocny
d0d652b427 Upload New File 2019-12-03 14:12:15 +00:00
Tim Mocny
231e048936 Upload New File 2019-12-03 14:11:47 +00:00
Tim Mocny
e95982a043 Upload New File 2019-12-03 14:10:52 +00:00
Tim Mocny
1a7b2677f3 Upload New File 2019-12-03 14:10:24 +00:00
Tim Mocny
b77bb5768d Upload New File 2019-12-03 14:09:52 +00:00
Tim Mocny
2be9fc65b1 Delete moss_viper.txt 2019-12-03 14:08:41 +00:00
Tim Mocny
c4e04fe003 Delete hydras_growth.txt 2019-12-03 14:07:56 +00:00
Tim Mocny
964e06b8cb Delete dreamshaper_shaman.txt 2019-12-03 14:07:26 +00:00
Tim Mocny
78d6847db5 Delete dreadful_apathy.txt 2019-12-03 14:07:11 +00:00
Tim Mocny
25d6ce6763 Delete aspect_of_lamprey.txt 2019-12-03 14:06:39 +00:00
Tim Mocny
fc26b5d3a9 Delete arena_trickster.txt 2019-12-03 14:06:04 +00:00
Tim Mocny
9e4dc631e8 Delete ashioks_erasure.txt 2019-12-03 14:04:49 +00:00
Tim Mocny
b6bff03ad3 Upload New File 2019-12-03 02:24:52 +00:00
Tim Mocny
394a365497 Upload New File 2019-12-03 02:24:12 +00:00
Tim Mocny
d5de2f387b Upload New File 2019-12-03 02:20:54 +00:00
Tim Mocny
5106de0abe Upload New File 2019-12-03 02:20:23 +00:00
Tim Mocny
510c81d251 Upload New File 2019-12-03 02:19:28 +00:00
Tim Mocny
b4437636a8 Upload New File 2019-12-03 02:18:38 +00:00
Tim Mocny
cc64997832 Upload New File 2019-12-03 02:17:39 +00:00
swordshine
c35afecce7 Merge branch 'patch' into 'master'
Update scripts by judotrente

See merge request core-developers/forge!2313
2019-12-02 04:26:41 +00:00
swordshine
2d9e8cc2af Update scripts by judotrente 2019-12-02 09:36:50 +08:00
swordshine
a9c4842b96 Merge branch 'spanishcards04' into 'master'
Translated to spanish previously untranslated first 2.000 cards of about 5.000

See merge request core-developers/forge!2312
2019-12-02 01:35:48 +00:00
Peter
c8b21a12ed Translated to spanish previously untranslated first 2.000 cards of about 5.000. Fixed some accutes in previous translation. 2019-12-01 20:08:12 +01:00
swordshine
d0a75c0943 Merge branch 'spanishcards03' into 'master'
Translated to spanish previously untranslated first 1.000 cards of about 5.000.

See merge request core-developers/forge!2310
2019-12-01 04:38:30 +00:00
swordshine
c75d3b2d04 Merge branch 'updatedspanish01' into 'master'
Updated spanish translations

See merge request core-developers/forge!2311
2019-12-01 04:38:19 +00:00
Peter
50d8c03f8b Commited wrong file 2019-11-30 14:45:08 +01:00
Peter
dde3e08e6a Updated spanish translations 2019-11-30 14:39:46 +01:00
Peter
2dd3385849 Merge branch 'master' of https://git.cardforge.org/core-developers/forge into spanishcards03 2019-11-30 14:23:51 +01:00
Peter
bd6cc759c7 Translated to spanish previously untranslated first 1.000 cards. Improved some translations and some of them translated thanks to DeepL.com. Translations based and fixed from www.rebellion.es 2019-11-30 14:23:22 +01:00
Michael Kamensky
39f695f120 Merge branch 'master' into 'master'
Added puzzle PS_ELD6 - Possibility Storm - Throne of Eldraine 06

See merge request core-developers/forge!2309
2019-11-29 19:44:20 +00:00
Agetian
61839df123 - Added puzzle PS_ELD6. 2019-11-29 21:22:08 +03:00
Michael Kamensky
e6b3ec048a Merge branch 'newBranch' into 'master'
Add Unstable Set to ExtendedArt

See merge request core-developers/forge!2308
2019-11-29 13:40:51 +00:00
Anthony Calosa
4fb5e7d305 Add Unstable Set to ExtendedArt 2019-11-29 13:40:25 +08:00
swordshine
8d4a80089e Merge branch 'patch' into 'master'
Game Night 2019 edition file

See merge request core-developers/forge!2307
2019-11-28 09:50:34 +00:00
swordshine
539e321c53 Removed an unused token 2019-11-28 16:53:40 +08:00
swordshine
737700719a Add Game Night 2019 edition file 2019-11-28 16:50:52 +08:00
swordshine
b2bc26142a Update Thoughtbound Phantasm's description 2019-11-28 16:50:52 +08:00
swordshine
9a125bf4f5 Merge branch 'master' into 'master'
Game Night 2019 Mythics

See merge request core-developers/forge!2306
2019-11-28 08:33:01 +00:00
Tim Mocny
b53135d83a Game Night 2019 Mythics 2019-11-28 08:33:00 +00:00
Tim Mocny
86262f3810 Upload New File 2019-11-28 04:51:27 +00:00
Tim Mocny
9237de38df Upload New File 2019-11-28 04:50:48 +00:00
Tim Mocny
18d5d7da27 Delete fiendish_duo.txt 2019-11-28 04:50:14 +00:00
Tim Mocny
cba653d891 Upload New File 2019-11-28 04:47:34 +00:00
Tim Mocny
9ea265625e Update highcliff_felidar.txt 2019-11-28 04:46:58 +00:00
Tim Mocny
d4aa0ecae4 Upload New File 2019-11-28 04:46:02 +00:00
Tim Mocny
bb5ee52133 Upload New File 2019-11-28 04:44:54 +00:00
Tim Mocny
33d5d45d3c Upload New File 2019-11-28 04:42:51 +00:00
swordshine
87bad70939 Merge branch 'master' into 'master'
translate some controller button

See merge request core-developers/forge!2305
2019-11-27 14:25:51 +00:00
CCTV-1
047bc9c5cd remove duplicate keys 2019-11-27 13:36:07 +08:00
CCTV-1
f098e333d2 translate some controller button 2019-11-27 13:32:05 +08:00
swordshine
785835632d Merge branch 'byebyeoko' into 'master'
Updated standard deckgen data - no more ELKs

See merge request core-developers/forge!2304
2019-11-27 01:43:56 +00:00
austinio7116
360b72fd91 Updated standard deckgen data - no more ELKs
(cherry picked from commit 42eafee)
2019-11-26 20:36:59 +00:00
Michael Kamensky
19082049e7 Merge branch 'master' into 'master'
Better AI logic spec for Heraldic Banner

Closes #1207

See merge request core-developers/forge!2302
2019-11-25 16:26:57 +00:00
Agetian
eacc29672f - Better AI logic spec for Heraldic Banner 2019-11-25 19:19:15 +03:00
Agetian
c7b6ac1670 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-11-25 12:40:04 +03:00
swordshine
0aa54cea23 Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!2300
2019-11-25 05:57:04 +00:00
Michael Kamensky
9fe50f6c66 Merge branch 'newBranch' into 'master'
Fix path seperator, Fix newdeck editor

See merge request core-developers/forge!2301
2019-11-25 05:27:44 +00:00
Anthony Calosa
2bb84cf79f Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-25 13:04:29 +08:00
Anthony Calosa
8d0ade1eab Fix path seperator
(android now works for filtering download if you have existing fullborder cards)

Fix newdeck editor not loading if a user deleted unwanted edition file
(TODO: Don't include setless cards on deck editor...)
2019-11-25 13:01:19 +08:00
CCTV-1
242c7c3b21 update simplified chinese translation 2019-11-25 12:36:07 +08:00
CCTV-1
ff272b89fd added missing translation label 2019-11-25 12:35:13 +08:00
Michael Kamensky
4e83e73dca Merge branch 'master' into 'master'
Update CHANGES.txt

See merge request core-developers/forge!2299
2019-11-24 05:01:49 +00:00
Michael Kamensky
7d1ffa4e90 Update CHANGES.txt 2019-11-24 05:01:49 +00:00
Agetian
aab83d882a - Updating CHANGES.txt, part 2. 2019-11-24 07:56:59 +03:00
Agetian
4f69b673bf Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-11-24 07:54:14 +03:00
Michael Kamensky
7d17e58e1f Merge branch 'newBranch' into 'master'
Update additional check

See merge request core-developers/forge!2298
2019-11-24 04:53:54 +00:00
Agetian
3d6677bbd5 - Update CHANGES.txt. 2019-11-24 07:52:42 +03:00
Anthony Calosa
30d57a5745 Update 2019-11-24 12:35:12 +08:00
Michael Kamensky
2fb2b64d33 Merge branch 'newBranch' into 'master'
Update LQ Downloader

See merge request core-developers/forge!2297
2019-11-24 04:35:01 +00:00
Anthony Calosa
1dc8fed6e8 Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-24 12:11:12 +08:00
Anthony Calosa
44c6605804 Update LQ downloader
(dont download full image equivalent to an existing fullborder images)
2019-11-24 12:05:45 +08:00
Michael Kamensky
560f8ac14a Merge branch 'master' into 'master'
Stack Addition Modal Panel

See merge request core-developers/forge!2294
2019-11-23 12:35:48 +00:00
Alessandro Coli
b99dea604d Labels for changes to stack effect notification panel 2019-11-23 12:07:36 +01:00
Alessandro Coli
f3ff419462 Changes for the stack effect notifications - request by Michael Kamensky
@Agetian in merge  request
https://git.cardforge.org/core-developers/forge/merge_requests/2294
2019-11-23 12:05:08 +01:00
Alessandro Coli
d79c2469ec Merge remote-tracking branch 'upstream/master' 2019-11-23 09:44:59 +01:00
swordshine
94de9e7cf2 Merge branch 'fix-broken-start' into 'master'
Fix start crash

See merge request core-developers/forge!2296
2019-11-23 02:40:36 +00:00
Chris H
a9d9c800b8 Fix start crash 2019-11-22 21:02:02 -05:00
swordshine
678d09558c Merge branch 'patch-5' into 'master'
Syr Konrad, the Grim: The Leave the Graveyard Trigger is only for your Graveyard

See merge request core-developers/forge!2295
2019-11-23 01:25:30 +00:00
Hans Mackowiak
831edab34b Syr Konrad, the Grim: The Leave the Graveyard Trigger is only for your Graveyard 2019-11-23 01:25:30 +00:00
Alessandro Coli
5b64b41f21 Merge remote-tracking branch 'upstream/master' 2019-11-22 13:40:18 +01:00
Alessandro Coli
78a5827976 Fixed some code indentations 2019-11-22 08:17:45 +01:00
Michael Kamensky
a9b6faeb7c Merge branch 'remove_some_build_warnings' into 'master'
Remove some build warnings

See merge request core-developers/forge!2219
2019-11-22 04:56:48 +00:00
Ryan1729
bebc58d91d Merge branch 'master' of git.cardforge.org:core-developers/forge into remove_some_build_warnings
# Conflicts:
#	forge-gui-mobile/src/forge/assets/FSkin.java
2019-11-21 10:24:52 -07:00
Alessandro Coli
2c70b8b7e0 End of the stack modal panel implementation, rollback about the Wild
Quests implementation
2019-11-21 16:49:50 +01:00
Alessandro Coli
07d7b7cb58 Merge remote-tracking branch 'upstream/master' 2019-11-21 16:35:45 +01:00
Anthony Calosa
4c80dd3c42 Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-20 21:13:35 +08:00
swordshine
0b436479be Merge branch 'spanishcards02' into 'master'
Spanish Cards Translations. Fixed some translations. Translated to Ardent Soldier

See merge request core-developers/forge!2293
2019-11-20 05:11:08 +00:00
swordshine
036cd90516 Merge branch 'master' into 'master'
Update the B&R list (Nov 18, 2019)

See merge request core-developers/forge!2292
2019-11-20 05:10:55 +00:00
Ryan1729
f0076117df respond to code review 2019-11-19 19:38:51 -07:00
klaxnek
f8b4a843c5 Spanish CardS Translations. Fixed some translations. Translated to Ardent Soldier 2019-11-19 22:39:00 +01:00
Agetian
5a24092dc0 - Update the B&R list (Nov 18, 2019) 2019-11-19 16:18:56 +03:00
Michael Kamensky
bf1c32f12a Merge branch 'master' into 'master'
Added puzzle PS_ELD5 - Possibility Storm - Throne of Eldraine 05.

See merge request core-developers/forge!2291
2019-11-19 10:55:15 +00:00
Agetian
f0fb6539ef - Added puzzle PS_ELD5. 2019-11-19 11:59:59 +03:00
swordshine
a85a761bee Merge branch 'spanishcards01' into 'master'
Spanish cards translation: from Abandon Hope to Altar of Bone.

See merge request core-developers/forge!2290
2019-11-19 01:11:05 +00:00
swordshine
2c61dc9a75 Merge branch 'patch-4' into 'master'
Update Deckmasters Garfield vs. Finkel.txt

See merge request core-developers/forge!2289
2019-11-19 01:10:55 +00:00
klaxnek
9516929495 Restore newline at end of file 2019-11-19 01:57:08 +01:00
klaxnek
5350a4c476 Spanish cards translation: from Abandon Hope to Altar of Bone.
Updated spanish card translations from Scryfall.
2019-11-19 01:54:10 +01:00
Alessandro Coli
26dc246c52 Merge remote-tracking branch 'upstream/master' 2019-11-18 13:44:34 +01:00
Anthony Calosa
ad6716f00e Update Deckmasters Garfield vs. Finkel.txt 2019-11-18 07:17:34 +00:00
Hans Mackowiak
dffb63d95f Merge branch 'newBranch' into 'master'
Fix declare attackers

Closes #1205

See merge request core-developers/forge!2288
2019-11-18 04:19:10 +00:00
Anthony Calosa
5069c71dd8 Fix declare attackers 2019-11-18 04:19:10 +00:00
Anthony Calosa
287abb9f37 Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-18 12:09:22 +08:00
Anthony Calosa
7f8325fb6c Fix declare attackers 2019-11-18 12:06:59 +08:00
swordshine
6c38674716 Merge branch 'german-translation' into 'master'
german translation update

See merge request core-developers/forge!2287
2019-11-18 00:51:47 +00:00
swordshine
54fdb69e88 Merge branch 'patch-2' into 'master'
Update proteus_staff.txt Remember Target Controller not Owner.

See merge request core-developers/forge!2283
2019-11-18 00:51:30 +00:00
Computica
068cee2326 Update proteus_staff.txt Remember Target Controller not Owner. 2019-11-18 00:51:29 +00:00
Alessandro Coli
e9c2e1034c Merge remote-tracking branch 'upstream/master' 2019-11-17 17:48:03 +01:00
Alessandro Coli
f453c2af78 Labels for the preferences of the new stack addition modal popup 2019-11-17 17:42:51 +01:00
Alessandro Coli
cc3c631cad Introduction of modal popup to notify when something is added to the
stack, including images for cards, complete with sources and targets.
2019-11-17 17:26:31 +01:00
Dagin Svezek
7cfd060ed8 Update de-DE.properties from forum 2019-11-17 09:18:03 +00:00
swordshine
c77f5e9b61 Merge branch 'newBranch' into 'master'
Fix images not loading when missing suffix

See merge request core-developers/forge!2285
2019-11-17 06:46:03 +00:00
swordshine
f18bd75257 Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!2286
2019-11-17 06:45:58 +00:00
CCTV-1
555a1f8411 update simplified chinese translation 2019-11-17 11:11:41 +08:00
Anthony Calosa
c600dc3b4f Update 2019-11-17 08:10:29 +08:00
Anthony Calosa
0f138a1bf5 Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-17 08:06:43 +08:00
Anthony Calosa
1c19f36637 Fix cards like planes/phenomenon not loading an existing images that have .full/.fullborder 2019-11-17 08:02:11 +08:00
Michael Kamensky
d19973208c Merge branch 'newBranch' into 'master'
Add support for "fullborder" cards

See merge request core-developers/forge!2284
2019-11-16 15:20:12 +00:00
Anthony Calosa
0a9078a2ec Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-16 17:51:04 +08:00
Anthony Calosa
bf2368ee44 Update 2019-11-16 17:44:56 +08:00
Anthony Calosa
d0a23fbf14 Add support for ".fullborder" cards
(to differentiate full image cards and full image cards with full borders,
so it will only crop full image cards with full borders)
2019-11-16 17:36:30 +08:00
Michael Kamensky
015f561740 Merge branch 'translation11' into 'master'
Translation: All Abilities Triggers in Stack & Initial GameLogFormatter.

See merge request core-developers/forge!2252
2019-11-16 09:06:27 +00:00
klaxnek
cff4a29f1c Merge branch 'master' of https://git.cardforge.org/core-developers/forge into translation11
 Conflicts:
	forge-gui/res/languages/de-DE.properties
	forge-gui/res/languages/en-US.properties
	forge-gui/res/languages/es-ES.properties
	forge-gui/res/languages/zh-CN.properties
2019-11-16 09:59:06 +01:00
Michael Kamensky
f50ee0ba36 Merge branch 'newBranch' into 'master'
Fix permanents not updating/moving to its row when animated to creature.

See merge request core-developers/forge!2282
2019-11-15 06:04:31 +00:00
Anthony Calosa
fe5d8e537c Fix game pause 2019-11-15 11:14:54 +08:00
Anthony Calosa
d943891e4f additional check (prevent npe on mojhosto) 2019-11-15 10:11:57 +08:00
Anthony Calosa
da3d1d7099 Update 2019-11-15 08:56:35 +08:00
Anthony Calosa
86dd21ae7b Fix permanents not updating when animated to creature. 2019-11-15 06:57:43 +08:00
Hans Mackowiak
2d30184f76 Merge branch 'combatPhaseHandlerFix' into 'master'
Combat: initConstraints before Declare Attackers

See merge request core-developers/forge!2281
2019-11-14 11:12:31 +00:00
Hans Mackowiak
271063934a Combat: initConstraints before Declare Attackers 2019-11-14 11:12:31 +00:00
Alessandro Coli
3fb601adfb Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-11-13 19:23:30 +01:00
swordshine
757e11af42 Merge branch 'patch-8' into 'master'
November 11, 2019 Pioneer Banned Announcement

See merge request core-developers/forge!2280
2019-11-13 00:54:19 +00:00
Hans Mackowiak
7ec12fb58d November 11, 2019 Pioneer Banned Announcement 2019-11-13 00:54:18 +00:00
Hans Mackowiak
6c9cf02884 Merge branch 'newBranch' into 'master'
Fix Amass ability (Woodland Champion and Dreadhorde Invasion)

See merge request core-developers/forge!2279
2019-11-12 12:13:36 +00:00
Anthony Calosa
0c904e208a Add AmassTrigger Test
(If Woodland Champion will trigger from Eternal Skylord Amass Ability)
2019-11-12 19:49:01 +08:00
swordshine
d12a898333 Merge branch 'seven-dwarves' into 'master'
Add 7 Dwarves

See merge request core-developers/forge!2278
2019-11-12 07:52:57 +00:00
Anthony Calosa
0e665dc673 Fix Amass ability (Woodland Champion and Dreadhorde Invasion) 2019-11-12 13:31:16 +08:00
tehdiplomat
02299a0400 Add 7 Dwarves 2019-11-11 14:12:49 -05:00
Michael Kamensky
86d7069626 Merge branch 'master' into 'master'
Preparing Forge for Android publish 1.6.30.001 [incremental].

See merge request core-developers/forge!2277
2019-11-11 05:17:32 +00:00
Agetian
776b865761 - Preparing Forge for Android publish 1.6.30.001 [incremental]. 2019-11-11 08:10:57 +03:00
Blacksmith
a3a4ef22df Clear out release files in preparation for next release 2019-11-11 02:50:48 +00:00
Blacksmith
c2db78baf7 [maven-release-plugin] prepare for next development iteration 2019-11-11 02:45:55 +00:00
Blacksmith
9a30d78f54 [maven-release-plugin] prepare release forge-1.6.30 2019-11-11 02:45:50 +00:00
Blacksmith
db98ce160b Update README.txt for release 2019-11-11 02:43:16 +00:00
Sol
ce8b5b53e0 Merge branch 'update-release-files' into 'master'
Update Release files

See merge request core-developers/forge!2276
2019-11-11 02:32:02 +00:00
Sol
0c4055726c Update ANNOUNCEMENTS.txt 2019-11-11 02:26:43 +00:00
Michael Kamensky
64ae4bae0c Merge branch 'newBranch' into 'master'
Update some GUI elements on Mobile Networkplay

See merge request core-developers/forge!2274
2019-11-10 06:21:46 +00:00
Anthony Calosa
044cc793e8 Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-09 21:04:48 +08:00
Alessandro Coli
ca81b2b55c Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-11-09 12:59:40 +01:00
Michael Kamensky
e310dc30d7 Merge branch 'pioneerdeckgen' into 'master'
Updated standard deckgen data

See merge request core-developers/forge!2275
2019-11-09 11:41:28 +00:00
Anthony Calosa
f239755249 Fix "controls" when alternating human vs ai, then ai vs ai play on mobile forge,
update refreshfield, update targeting arrows on 3 to 4 players
(shows attacked player on 3/4 player match...)
2019-11-09 19:28:21 +08:00
austinio7116
57686c0554 Updated standard deckgen data
(cherry picked from commit 75f1a60)
2019-11-09 09:04:06 +00:00
Anthony Calosa
5ecde572c3 Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-09 06:27:14 +08:00
Anthony Calosa
23e9974950 Update some GUI elements on networkplay -> client 2019-11-09 06:20:13 +08:00
Anthony Calosa
ec98d128f1 prevent npe mojhosto 2019-11-09 06:16:58 +08:00
swordshine
5c07951604 Merge branch 'german-translation' into 'master'
fix typos

See merge request core-developers/forge!2273
2019-11-08 00:43:36 +00:00
Dagin Svezek
7be34625f6 fix typos 2019-11-07 14:03:24 +00:00
Alessandro Coli
f15762b746 Introduction of "wild duels" in quest 2019-11-07 13:26:22 +01:00
Alessandro Coli
e365e78756 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-11-07 13:23:11 +01:00
swordshine
5f6cb893ef Merge branch 'master' into 'master'
Added puzzle PS_ELD4. Added the relevant functionality to GateState and fixed a NPE.

See merge request core-developers/forge!2271
2019-11-07 06:17:25 +00:00
Agetian
3b636be2fe Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-11-07 08:10:19 +03:00
Agetian
1af940b034 - Simpler adventure card detection. 2019-11-07 08:08:03 +03:00
Agetian
8cb1789e60 - Added puzzle PS_ELD4.
- Added OnAdventure functionality to game states.
- Fixed a NPE when dev-adding a card to exile.
2019-11-07 08:06:31 +03:00
Michael Kamensky
01782de3a6 Merge branch 'patch-7' into 'master'
Update de-DE.properties from forum (twosat user)

See merge request core-developers/forge!2270
2019-11-07 04:02:59 +00:00
Michael Kamensky
3a7e35c51d Merge branch 'cardtranslationforgecore' into 'master'
Moved CardTranslation to forge-core.

See merge request core-developers/forge!2253
2019-11-07 04:02:57 +00:00
swordshine
bff24a1d3d Merge branch 'patch-6' into 'master'
Replace sprite_icons.png in darkred skin

See merge request core-developers/forge!2269
2019-11-07 00:52:17 +00:00
swordshine
8a075000a2 Merge branch 'patch-5' into 'master'
Replace bg_splash.png in Darkred skin

See merge request core-developers/forge!2268
2019-11-07 00:52:05 +00:00
Churrufli
5e0761d085 Update de-DE.properties from forum (twosat user) 2019-11-06 23:33:57 +00:00
Churrufli
2184ddf1bb Replace sprite_icons.png in darkred skin 2019-11-06 17:54:06 +00:00
Churrufli
fd032f6ccd Replace bg_splash.png in Darkred skin 2019-11-06 17:52:30 +00:00
Hans Mackowiak
164c819523 Merge branch 'patch-5' into 'master'
Update dread_warlock: added Warlock type

See merge request core-developers/forge!2267
2019-11-06 15:06:41 +00:00
Hans Mackowiak
987043ead2 Update dread_warlock: added Warlock type 2019-11-06 15:06:41 +00:00
swordshine
4aa9c224d0 Merge branch 'pioneerrandomquestworld' into 'master'
Pioneer random quest mode

See merge request core-developers/forge!2266
2019-11-06 08:32:13 +00:00
swordshine
2ac6fa4542 Merge branch 'elddeckgendata' into 'master'
Updated Modern and Standard Deckgen Data

See merge request core-developers/forge!2265
2019-11-06 08:31:48 +00:00
swordshine
2797f95cd3 Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!2264
2019-11-06 08:31:33 +00:00
austinio7116
5eb9be6248 Pioneer random quest mode 2019-11-06 08:07:21 +00:00
austinio7116
23eebf9037 Updated Modern and Standard Deckgen Data 2019-11-06 06:17:41 +00:00
CCTV-1
b59adab68d added missing translation label 2019-11-06 10:45:44 +08:00
CCTV-1
b2d44105be update simplified chinese translation 2019-11-06 10:44:19 +08:00
swordshine
2235546f2a Merge branch 'translation10' into 'master'
More translations: New menu settings, Search, Priority, PayMana, Discard, Order, Exile, Delve, ...

See merge request core-developers/forge!2251
2019-11-06 01:04:53 +00:00
swordshine
59d104f68b Merge branch 'pioneerdeckgen' into 'master'
Pioneer deckgen

See merge request core-developers/forge!2263
2019-11-06 01:04:07 +00:00
swordshine
6abdfd391d Merge branch '1191-format-pioneer' into 'master'
Resolve "Format: pioneer"

Closes #1191

See merge request core-developers/forge!2250
2019-11-06 01:04:02 +00:00
Hans Mackowiak
a6ff0b5b10 Resolve "Format: pioneer" 2019-11-06 01:04:02 +00:00
austinio7116
02969cfe5b Pioneer archetype deck generation added to UI 2019-11-05 23:02:58 +00:00
austinio7116
c6a2c35850 Pioneer initial meta
(cherry picked from commit af89555)
2019-11-05 22:37:00 +00:00
maustin
4c0a71f37d Merge branch '1191-format-pioneer' of https://git.cardforge.org/core-developers/forge into pioneerdeckgen 2019-11-05 22:36:19 +00:00
Hans Mackowiak
74e3bd1895 Pionier: November 2019 Banned cards 2019-11-05 12:05:34 +00:00
Hans Mackowiak
121c9f5012 add PioneerPredicate to magicDb 2019-11-05 08:36:19 +00:00
Hans Mackowiak
7b1cd816b7 Update formats 2019-11-05 08:25:43 +00:00
Alessandro Coli
02ae8f8108 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-11-05 08:31:54 +01:00
Michael Kamensky
6a76cc8bc6 Merge branch 'newBranch' into 'master'
Update Network and Dependency files

See merge request core-developers/forge!2259
2019-11-04 16:44:48 +00:00
swordshine
9b9c38126e Merge branch '1200-memory-theft-allows-discarding-of-a-land' into 'master'
Resolve "Memory Theft allows discarding of a land"

Closes #1200

See merge request core-developers/forge!2261
2019-11-04 14:26:01 +00:00
Michael Kamensky
e4ee0c768f Merge branch 'ManaPoolHiddenCleanup' into 'master'
ManaPool: no hidden keyword there, so no need for extra cleanup

See merge request core-developers/forge!2262
2019-11-04 13:46:08 +00:00
Hans Mackowiak
340de153c8 ManaPool: no hidden keyword there, so no need for extra cleanup 2019-11-04 13:46:08 +00:00
Hans Mackowiak
2bf477102d Update memory_theft: add nonLand part 2019-11-04 09:42:30 +00:00
Anthony Calosa
42a15b40b3 Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-11-04 07:47:27 +08:00
Michael Kamensky
a2589cd433 Merge branch 'patch-5' into 'master'
Update net-decks.txt adding Current Pioneer Decks

See merge request core-developers/forge!2260
2019-11-03 13:49:24 +00:00
Churrufli
7ec7025ed4 Update net-decks.txt adding Current Pioneer Decks 2019-11-03 07:40:41 +00:00
Anthony Calosa
5edeb6df94 removed config (log4j2 uses alternate config via xml) 2019-11-02 10:12:59 +08:00
Anthony Calosa
001a1981cf update log4j 1.2.17 -> log4j 2.11.2
(log4j 2.12.x latest needs higher Android API)
2019-11-02 10:07:13 +08:00
Anthony Calosa
446fb59473 some device are looking for this file so include it on storage
java.io.FileNotFoundException:
/storage/emulated/0/Forge/src/main/resources/log4jConfig.config
(No such file or directory)
2019-11-01 21:42:24 +08:00
Anthony Calosa
3b58d6df42 refactor rename 2019-11-01 17:46:14 +08:00
Anthony Calosa
a80c683901 support android 6 (slow networkplay) tested with two Android 6 device 2019-11-01 17:34:42 +08:00
Anthony Calosa
a5b65eaaed add old de/serialization for android 7.1 and below 2019-11-01 15:03:30 +08:00
Anthony Calosa
31182289b7 Update dependency and use custom de/encoder for netty 2019-11-01 12:41:30 +08:00
Anthony Calosa
85eb740264 aifixes NPE 2019-11-01 12:40:40 +08:00
Anthony Calosa
4318e23a40 support UST extended art 2019-11-01 12:39:57 +08:00
Alessandro Coli
b1b01f2426 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-31 14:10:41 +01:00
Hans Mackowiak
4ca7352d5c Merge branch 'fix-staticabilitycanttarget' into 'master'
Fix StaticAbilityCantTarget check not calling the common routine.

See merge request core-developers/forge!2258
2019-10-29 19:46:37 +00:00
Michael Kamensky
84905bd726 Fix StaticAbilityCantTarget check not calling the common routine. 2019-10-29 19:46:37 +00:00
swordshine
18e16368be Merge branch 'master' into 'master'
Added puzzle PS_ELD3 - Possibility Storm - Throne of Eldraine 03

See merge request core-developers/forge!2257
2019-10-29 07:51:49 +00:00
Agetian
7ed84c4c3f - Added puzzle PS_ELD3. 2019-10-29 09:34:39 +03:00
Michael Kamensky
f334211395 Merge branch 'newBranch' into 'master'
Mobile Forge: Card Sleeves & Round Border Refactor

See merge request core-developers/forge!2255
2019-10-26 14:46:09 +00:00
Alessandro Coli
164ca8541e Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-25 13:41:26 +02:00
Anthony Calosa
42f4126aff Prepare Sleeve for Desktop... 2019-10-25 15:37:13 +08:00
Anthony Calosa
5790e29daa Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-10-25 14:22:07 +08:00
Anthony Calosa
88a4a2c6cf Update 2019-10-25 14:21:25 +08:00
swordshine
90b72fc11e Merge branch '1194-resolute-rider-has-incorrect-activated-ability-cost' into 'master'
Resolve "Resolute Rider has incorrect activated ability cost"

Closes #1194

See merge request core-developers/forge!2256
2019-10-24 08:46:54 +00:00
Hans Mackowiak
00391df1f0 Fix Resolute Rider 2019-10-24 07:46:00 +00:00
swordshine
15de0c0bba Merge branch 'master' into 'master'
Added puzzle PS_ELD2 - Possibility Storm - Throne of Eldraine 02

See merge request core-developers/forge!2254
2019-10-24 01:00:06 +00:00
Agetian
a2fdce9be9 - Added puzzle PS_ELD2. 2019-10-23 22:42:52 +03:00
klaxnek
fa6fce9589 Moved CardTranslation to forge-core. We need it to translate card info in forge-game. 2019-10-23 11:47:21 +02:00
Anthony Calosa
cc1f03fc94 Update 2019-10-23 12:57:36 +08:00
Anthony Calosa
8b25f6f129 Fix Foil Overlay when Round Border is enabled 2019-10-23 11:57:56 +08:00
Anthony Calosa
b37937421c Merge remote-tracking branch 'remotes/core/master' into newBranch 2019-10-23 11:52:52 +08:00
klaxnek
df7be19487 Translate Permanent Creature 2019-10-23 00:56:36 +02:00
klaxnek
f17d2bf7fc Translate Trigger Abilities. Finished. 2019-10-23 00:24:15 +02:00
klaxnek
3369db8150 Translated Trigger Abilities (1) 2019-10-22 22:45:03 +02:00
klaxnek
408c9df2df Mobile: Merged duplicated switch 2019-10-22 21:24:13 +02:00
klaxnek
63b6d665a1 Mobile: Merge duplicated case in switch (QuestDraft with Draft) 2019-10-22 21:22:40 +02:00
klaxnek
cfc8b7fb28 Mobile: Translate Minor things 2019-10-22 21:18:30 +02:00
klaxnek
1497345a79 Translate GameLogFormatter. Mulligan and didn't attack 2019-10-22 20:29:50 +02:00
klaxnek
bbd2940b63 Translated scry log 2019-10-22 14:40:03 +02:00
klaxnek
b9c65fb8ee Translated Empty (Stack) 2019-10-22 14:22:03 +02:00
klaxnek
0b37b4e543 Translate Stack text 2019-10-22 14:20:46 +02:00
klaxnek
4c3e4f2170 Translate PlayerControllerHuman.java. Discard, order of cards, delve, exile. 2019-10-22 13:23:57 +02:00
klaxnek
dc12c50c1a Forgot lblCleanupPhase 2019-10-22 12:56:53 +02:00
klaxnek
2b986f5bac Translate PlayerControllerHuman.java. Discard cards 2019-10-22 12:50:15 +02:00
klaxnek
2accf7543e Translated Input Pay Mana. Expanded sentences in order to translate them. 2019-10-22 12:08:28 +02:00
klaxnek
ba37189410 Translate InputPassPriority.java 2019-10-22 11:36:36 +02:00
Hans Mackowiak
d578eee402 Add Pioneer Format 2019-10-22 08:47:00 +00:00
Hans Mackowiak
5107d89ef5 GameFormat: add Píoneer 2019-10-22 08:45:07 +00:00
Hans Mackowiak
72b8b5c98e Update StaticData: are the Predicates even used? 2019-10-22 08:42:12 +00:00
Anthony Calosa
402885391f Card Sleeves 2019-10-22 16:12:03 +08:00
klaxnek
b4d153ab3b Translate Search word in search filters 2019-10-22 09:51:03 +02:00
klaxnek
1a0cb62ac8 Translate Preload Extended Art 2019-10-22 09:42:16 +02:00
klaxnek
c0baf70c59 Translate new Settings menú 2019-10-22 09:37:05 +02:00
swordshine
166cf2623c Merge branch 'master' into 'master'
update Simplified Chinese translation

See merge request core-developers/forge!2249
2019-10-22 06:02:17 +00:00
CCTV-1
8567b69073 update 'Draft, Gauntlet and Puzzle Screens' Simplified Chinese translation 2019-10-22 13:01:37 +08:00
swordshine
3a4271e66d Merge branch 'updatetranslation' into 'master'
update new settings translation

See merge request core-developers/forge!2246
2019-10-22 03:05:25 +00:00
swordshine
8b723aebd9 Merge branch 'master' into 'master'
update BitmapFontWriter.java to latest version

See merge request core-developers/forge!2243
2019-10-22 03:05:18 +00:00
swordshine
eb59d6c86b Merge branch 'translation09' into 'master'
Mobile: Translate Draft, Gauntlet and Puzzle Screens.

See merge request core-developers/forge!2228
2019-10-22 03:05:04 +00:00
Sol
a291b75dd9 Merge branch 'patch-4' into 'master'
Questing Beast: fix Damage Trigger

See merge request core-developers/forge!2247
2019-10-22 00:41:25 +00:00
Hans Mackowiak
f1a76e1e76 Questing Beast: fix Damage Trigger 2019-10-22 00:41:25 +00:00
Sol
a391f7414f Merge branch 'updateBanList' into 'master'
BanList 2019-10-21

See merge request core-developers/forge!2248
2019-10-22 00:40:49 +00:00
Hans Mackowiak
299de54ba5 BanList 2019-10-21 2019-10-22 00:40:49 +00:00
CCTV-1
37bae14dfd update Simplified Chinese characters list 2019-10-19 10:26:43 +08:00
CCTV-1
d780aa43d4 update new settings translation 2019-10-18 18:30:59 +08:00
CCTV-1
91534776d1 convert \t to four spaces,makes the diff able to read 2019-10-17 11:07:00 +08:00
Alessandro Coli
ef82e11c53 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-16 14:10:58 +02:00
CCTV-1
31bb611ecf update BitmapFontWriter.java to latest version(https://github.com/libgdx/libgdx/blob/master/extensions/gdx-tools/src/com/badlogic/gdx/tools/bmfont/BitmapFontWriter.java) 2019-10-15 18:35:16 +08:00
Alessandro Coli
762481e28a Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-13 17:58:46 +02:00
Alessandro Coli
25194c7abe Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-12 11:07:46 +02:00
Alessandro Coli
967af29c47 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-11 18:34:39 +02:00
Alessandro Coli
ae9ee57d79 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-10 19:50:02 +02:00
Alessandro Coli
17706bd5e4 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-09 22:20:15 +02:00
Alessandro Coli
9ed54f726c Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-08 14:11:00 +02:00
Peter
53c88f0302 Added to contributors XD 2019-10-07 22:40:45 +02:00
Peter
ad1cc78578 Mobile: Translate Draft, Gauntlet and Puzzle Screens.
Fixed crash in LoadGameMenu (wrong translated enum...)
2019-10-07 22:39:00 +02:00
Alessandro Coli
f94a613eef Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-07 13:56:51 +02:00
Alessandro Coli
e6fa89206a Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-06 11:40:00 +02:00
Ryan1729
b010da744c progagate type information through to get rid of warning 2019-10-06 02:13:37 -06:00
Ryan1729
d53eb3a3fe pull all URLDecoder.decode calls into one method and suppress the warning since changing it appears to have broken things before. 2019-10-06 02:00:12 -06:00
Ryan1729
ea25ad2c3b use setVisible(true) instead of show to get rid of the warning 2019-10-06 01:49:06 -06:00
Ryan1729
ac7f34d3f5 Cast to Object as recommended by warning 2019-10-06 01:40:15 -06:00
Ryan1729
4969547938 inline capitalize 2019-10-06 01:36:37 -06:00
Ryan1729
6808be7a42 route all capitalize calls through a new method 2019-10-06 01:33:52 -06:00
Ryan1729
70986149ef do less stuff since we know what the arguments will be. 2019-10-06 01:05:42 -06:00
Ryan1729
bc859b3e52 inline wrap 2019-10-06 00:56:12 -06:00
Ryan1729
eb376b8eb3 pull all still needed WordUtils.wrap calls into one method 2019-10-06 00:55:29 -06:00
Alessandro Coli
9fce28bed7 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-04 23:13:19 +02:00
Alessandro Coli
0bcc0e9248 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-03 20:44:18 +02:00
Alessandro Coli
aecee3cea2 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-02 13:30:06 +02:00
Alessandro Coli
3432b9fdcd Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-10-01 18:56:14 +02:00
Alessandro Coli
8bde4d5f92 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-09-29 09:29:19 +02:00
Alessandro Coli
67c596726f Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-09-22 16:15:23 +02:00
Alessandro Coli
60fb1c0d02 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-09-21 12:28:37 +02:00
Alessandro Coli
3261e1a260 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-09-18 19:51:31 +02:00
Alessandro Coli
2e1fa8fa40 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-09-15 14:10:20 +02:00
Alessandro Coli
cadb2ae791 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-09-08 09:19:21 +02:00
Alessandro Coli
2d3990df81 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-31 18:28:27 +02:00
Alessandro Coli
23d4904ee7 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-28 11:34:45 +02:00
Alessandro Coli
f66f51b25e Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-23 14:33:15 +02:00
Alessandro Coli
826ff27bd1 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-21 17:17:24 +02:00
Alessandro Coli
132aa8d49a Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-19 12:49:07 +02:00
Alessandro Coli
67148cd84c Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-15 09:51:35 +02:00
Alessandro Coli
941e3a7537 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-13 15:42:20 +02:00
Alessandro Coli
4487952c28 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-10 23:21:59 +02:00
Alessandro Coli
5089663b07 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-09 19:32:29 +02:00
Alessandro Coli
3ec5bfd4ff Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-08-05 14:46:22 +02:00
Alessandro Coli
adb1ef35d9 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-20 13:12:50 +02:00
Alessandro Coli
df85278a44 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-18 12:51:43 +02:00
Alessandro Coli
7a04669560 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-18 09:09:03 +02:00
Alessandro Coli
2e24a1bde4 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-14 13:35:09 +02:00
Alessandro Coli
16134cb857 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-14 09:03:33 +02:00
Alessandro Coli
d396a8755b Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-12 18:52:27 +02:00
Alessandro Coli
7617b95fae Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-10 20:23:03 +02:00
Alessandro Coli
ecdda5b678 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-07 15:55:17 +02:00
Alessandro Coli
2a16a29ea2 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-07-02 19:15:49 +02:00
Alessandro Coli
81d7bec2cd Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-06-29 10:10:20 +02:00
Alessandro Coli
87ffbe4c20 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-06-22 17:22:36 +02:00
Alessandro Coli
94a0fb20d2 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-06-16 08:54:25 +02:00
Alessandro Coli
7313670d8f Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-06-12 18:32:48 +02:00
Alessandro Coli
5047e7732c Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-06-01 18:48:35 +02:00
Alessandro Coli
98a459ac9c Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-05-30 12:34:52 +02:00
Alessandro Coli
eec3f05dc7 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-05-28 13:29:02 +02:00
Alessandro Coli
4db2fd4ee7 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-05-26 08:53:55 +02:00
Alessandro Coli
41e0760678 Riallineamento con branch master remoto 2019-05-26 08:52:02 +02:00
Alessandro Coli
36b9689cf8 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-05-25 17:17:16 +02:00
Alessandro Coli
c5b09815e3 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-05-24 13:40:56 +02:00
Alessandro Coli
3da7b0f8e6 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-05-21 20:38:45 +02:00
Alessandro Coli
1aa0d321cc Settings i like more for the AI 2019-05-21 20:37:30 +02:00
2620 changed files with 79781 additions and 21389 deletions

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>1.6.30-SNAPSHOT</version>
<version>1.6.35</version>
</parent>
<artifactId>forge-ai</artifactId>

View File

@@ -24,6 +24,7 @@ import forge.ai.ability.AnimateAi;
import forge.card.CardTypeView;
import forge.game.GameEntity;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.effects.ProtectEffect;
import forge.game.card.*;
@@ -464,7 +465,7 @@ public class AiAttackController {
final CardCollectionView beastions = ai.getCardsIn(ZoneType.Battlefield, "Beastmaster Ascension");
int minCreatures = 7;
for (final Card beastion : beastions) {
final int counters = beastion.getCounters(CounterType.QUEST);
final int counters = beastion.getCounters(CounterEnumType.QUEST);
minCreatures = Math.min(minCreatures, 7 - counters);
}
if (this.attackers.size() >= minCreatures) {
@@ -1065,7 +1066,7 @@ public class AiAttackController {
}
}
// if enough damage: switch to next planeswalker or player
if (damage >= pw.getCounters(CounterType.LOYALTY)) {
if (damage >= pw.getCounters(CounterEnumType.LOYALTY)) {
List<Card> pwDefending = combat.getDefendingPlaneswalkers();
boolean found = false;
// look for next planeswalker
@@ -1135,7 +1136,6 @@ public class AiAttackController {
// TODO Somehow subtract expected damage of other attacking creatures from enemy life total (how? other attackers not yet declared? Can the AI guesstimate which of their creatures will not get blocked?)
if (attacker.getCurrentPower() * Integer.parseInt(attacker.getSVar("NonCombatPriority")) < ai.getOpponentsSmallestLifeTotal()) {
// Check if the card actually has an ability the AI can and wants to play, if not, attacking is fine!
boolean wantability = false;
for (SpellAbility sa : attacker.getSpellAbilities()) {
// Do not attack if we can afford using the ability.
if (sa.isAbility()) {
@@ -1192,7 +1192,7 @@ public class AiAttackController {
if (isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2) {
numberOfPossibleBlockers += 1;
if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, false)
&& !(attacker.hasKeyword(Keyword.UNDYING) && attacker.getCounters(CounterType.P1P1) == 0)) {
&& !(attacker.hasKeyword(Keyword.UNDYING) && attacker.getCounters(CounterEnumType.P1P1) == 0)) {
canBeKilledByOne = true; // there is a single creature on the battlefield that can kill the creature
// see if the defending creature is of higher or lower
// value. We don't want to attack only to lose value
@@ -1365,21 +1365,12 @@ public class AiAttackController {
if (c.hasSVar("AIExertCondition")) {
if (!c.getSVar("AIExertCondition").isEmpty()) {
final String needsToExert = c.getSVar("AIExertCondition");
int x = 0;
int y = 0;
String sVar = needsToExert.split(" ")[0];
String comparator = needsToExert.split(" ")[1];
String compareTo = comparator.substring(2);
try {
x = Integer.parseInt(sVar);
} catch (final NumberFormatException e) {
x = CardFactoryUtil.xCount(c, c.getSVar(sVar));
}
try {
y = Integer.parseInt(compareTo);
} catch (final NumberFormatException e) {
y = CardFactoryUtil.xCount(c, c.getSVar(compareTo));
}
int x = AbilityUtils.calculateAmount(c, sVar, null);
int y = AbilityUtils.calculateAmount(c, compareTo, null);
if (Expressions.compare(x, comparator, y)) {
shouldExert = true;
}

View File

@@ -228,9 +228,9 @@ public class AiBlockController {
// 3.Blockers that can destroy the attacker and have an upside when dying
killingBlockers = getKillingBlockers(combat, attacker, blockers);
for (Card b : killingBlockers) {
if ((b.hasKeyword(Keyword.UNDYING) && b.getCounters(CounterType.P1P1) == 0) || b.hasSVar("SacMe")
|| (b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterType.TIME) == 1)
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterType.FADE) == 0)
if ((b.hasKeyword(Keyword.UNDYING) && b.getCounters(CounterEnumType.P1P1) == 0) || b.hasSVar("SacMe")
|| (b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterEnumType.TIME) == 1)
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterEnumType.FADE) == 0)
|| b.hasSVar("EndOfTurnLeavePlay")) {
blocker = b;
break;
@@ -299,8 +299,8 @@ public class AiBlockController {
final List<Card> blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
for (Card b : blockers) {
if ((b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterType.TIME) == 1)
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterType.FADE) == 0)
if ((b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterEnumType.TIME) == 1)
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterEnumType.FADE) == 0)
|| b.hasSVar("EndOfTurnLeavePlay")) {
blocker = b;
if (!ComputerUtilCombat.canDestroyAttacker(ai, attacker, blocker, combat, false)) {
@@ -851,7 +851,7 @@ public class AiBlockController {
damageToPW += ComputerUtilCombat.predictDamageTo((Card) def, pwatkr.getNetCombatDamage(), pwatkr, true);
}
}
if ((!onlyIfLethal && damageToPW > 0) || damageToPW >= def.getCounters(CounterType.LOYALTY)) {
if ((!onlyIfLethal && damageToPW > 0) || damageToPW >= def.getCounters(CounterEnumType.LOYALTY)) {
threatenedPWs.add((Card) def);
}
}
@@ -909,7 +909,7 @@ public class AiBlockController {
damageToPW += ComputerUtilCombat.predictDamageTo(pw, pwAtk.getNetCombatDamage(), pwAtk, true);
}
}
if (!isFullyBlocked && damageToPW >= pw.getCounters(CounterType.LOYALTY)) {
if (!isFullyBlocked && damageToPW >= pw.getCounters(CounterEnumType.LOYALTY)) {
for (Card chump : pwDefenders) {
if (chosenChumpBlockers.contains(chump)) {
combat.removeFromCombat(chump);
@@ -1281,7 +1281,8 @@ public class AiBlockController {
oppCreatureCount = ComputerUtil.countUsefulCreatures(attackersLeft.get(0).getController());
}
if (attacker.getOwner().equals(ai) && "6".equals(attacker.getSVar("SacMe"))) {
if (attacker != null && attacker.getOwner() != null)
if (attacker.getOwner().equals(ai) && "6".equals(attacker.getSVar("SacMe"))) {
// Temporarily controlled object - don't trade with it
// TODO: find a more reliable way to figure out that control will be reestablished next turn
return false;

View File

@@ -177,7 +177,7 @@ public class AiController {
&& CardFactoryUtil.isCounterable(host)) {
return true;
} else if ("ChaliceOfTheVoid".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)
&& host.getCMC() == c.getCounters(CounterType.CHARGE)) {
&& host.getCMC() == c.getCounters(CounterEnumType.CHARGE)) {
return true;
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)) {
String hostName = host.getName();
@@ -769,7 +769,7 @@ public class AiController {
return AiPlayDecision.CantPlayAi;
}
}
else if (sa.getPayCosts() != null){
else {
Cost payCosts = sa.getPayCosts();
ManaCost mana = payCosts.getTotalMana();
if (mana != null) {
@@ -858,7 +858,7 @@ public class AiController {
int neededMana = 0;
boolean dangerousRecurringCost = false;
Cost costWithBuyback = sa.getPayCosts() != null ? sa.getPayCosts().copy() : Cost.Zero;
Cost costWithBuyback = sa.getPayCosts().copy();
for (OptionalCostValue opt : GameActionUtil.getOptionalCostValues(sa)) {
if (opt.getType() == OptionalCost.Buyback) {
costWithBuyback.add(opt.getCost());
@@ -907,8 +907,8 @@ public class AiController {
public int compare(final SpellAbility a, final SpellAbility b) {
// sort from highest cost to lowest
// we want the highest costs first
int a1 = a.getPayCosts() == null ? 0 : a.getPayCosts().getTotalMana().getCMC();
int b1 = b.getPayCosts() == null ? 0 : b.getPayCosts().getTotalMana().getCMC();
int a1 = a.getPayCosts().getTotalMana().getCMC();
int b1 = b.getPayCosts().getTotalMana().getCMC();
// deprioritize SAs explicitly marked as preferred to be activated last compared to all other SAs
if (a.hasParam("AIActivateLast") && !b.hasParam("AIActivateLast")) {
@@ -927,12 +927,12 @@ public class AiController {
// deprioritize pump spells with pure energy cost (can be activated last,
// since energy is generally scarce, plus can benefit e.g. Electrostatic Pummeler)
int a2 = 0, b2 = 0;
if (a.getApi() == ApiType.Pump && a.getPayCosts() != null && a.getPayCosts().getCostEnergy() != null) {
if (a.getApi() == ApiType.Pump && a.getPayCosts().getCostEnergy() != null) {
if (a.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
a2 = a.getPayCosts().getCostEnergy().convertAmount();
}
}
if (b.getApi() == ApiType.Pump && b.getPayCosts() != null && b.getPayCosts().getCostEnergy() != null) {
if (b.getApi() == ApiType.Pump && b.getPayCosts().getCostEnergy() != null) {
if (b.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
b2 = b.getPayCosts().getCostEnergy().convertAmount();
}
@@ -956,8 +956,7 @@ public class AiController {
return 1;
}
if (a.getHostCard().equals(b.getHostCard()) && a.getApi() == b.getApi()
&& a.getPayCosts() != null && b.getPayCosts() != null) {
if (a.getHostCard().equals(b.getHostCard()) && a.getApi() == b.getApi()) {
// Cheaper Spectacle costs should be preferred
// FIXME: Any better way to identify that these are the same ability, one with Spectacle and one not?
// (looks like it's not a full-fledged alternative cost as such, and is not processed with other alt costs)
@@ -1479,7 +1478,7 @@ public class AiController {
}
for (SpellAbility sa : card.getSpellAbilities()) {
if (sa.getPayCosts() != null && sa.isAbility()
if (sa.isAbility()
&& sa.getPayCosts().getCostMana() != null
&& sa.getPayCosts().getCostMana().getMana().getCMC() > 0
&& (!sa.getPayCosts().hasTapCost() || !isTapLand)
@@ -1802,7 +1801,7 @@ public class AiController {
throw new UnsupportedOperationException("AI is not supposed to reach this code at the moment");
}
public CardCollection chooseCardsForEffect(CardCollectionView pool, SpellAbility sa, int min, int max, boolean isOptional) {
public CardCollection chooseCardsForEffect(CardCollectionView pool, SpellAbility sa, int min, int max, boolean isOptional, Map<String, Object> params) {
if (sa == null || sa.getApi() == null) {
throw new UnsupportedOperationException();
}
@@ -1835,7 +1834,7 @@ public class AiController {
default:
CardCollection editablePool = new CardCollection(pool);
for (int i = 0; i < max; i++) {
Card c = player.getController().chooseSingleEntityForEffect(editablePool, sa, null, isOptional);
Card c = player.getController().chooseSingleEntityForEffect(editablePool, sa, null, isOptional, params);
if (c != null) {
result.add(c);
editablePool.remove(c);
@@ -1986,6 +1985,35 @@ public class AiController {
return MyRandom.getRandom().nextBoolean();
}
public boolean chooseEvenOdd(SpellAbility sa) {
String aiLogic = sa.getParamOrDefault("AILogic", "");
if (aiLogic.equals("AlwaysEven")) {
return false; // false is Even
} else if (aiLogic.equals("AlwaysOdd")) {
return true; // true is Odd
} else if (aiLogic.equals("Random")) {
return MyRandom.getRandom().nextBoolean();
} else if (aiLogic.equals("CMCInHand")) {
CardCollectionView hand = sa.getActivatingPlayer().getCardsIn(ZoneType.Hand);
int numEven = CardLists.filter(hand, CardPredicates.evenCMC()).size();
int numOdd = CardLists.filter(hand, CardPredicates.oddCMC()).size();
return numOdd > numEven;
} else if (aiLogic.equals("CMCOppControls")) {
CardCollectionView hand = sa.getActivatingPlayer().getOpponents().getCardsIn(ZoneType.Battlefield);
int numEven = CardLists.filter(hand, CardPredicates.evenCMC()).size();
int numOdd = CardLists.filter(hand, CardPredicates.oddCMC()).size();
return numOdd > numEven;
} else if (aiLogic.equals("CMCOppControlsByPower")) {
// TODO: improve this to check for how dangerous those creatures actually are relative to host card
CardCollectionView hand = sa.getActivatingPlayer().getOpponents().getCardsIn(ZoneType.Battlefield);
int powerEven = Aggregates.sum(CardLists.filter(hand, CardPredicates.evenCMC()), Accessors.fnGetNetPower);
int powerOdd = Aggregates.sum(CardLists.filter(hand, CardPredicates.oddCMC()), Accessors.fnGetNetPower);
return powerOdd > powerEven;
}
return MyRandom.getRandom().nextBoolean(); // outside of any specific logic, choose randomly
}
public Card chooseCardToHiddenOriginChangeZone(ZoneType destination, List<ZoneType> origin, SpellAbility sa,
CardCollection fetchList, Player player2, Player decider) {
if (useSimulation) {

View File

@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import forge.card.CardType;
@@ -16,12 +17,14 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.cost.*;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.TextUtil;
import forge.util.collect.FCollectionView;
@@ -104,6 +107,24 @@ public class AiCostDecision extends CostDecisionMakerBase {
}
return PaymentDecision.card(randomSubset);
}
else if (type.equals("DifferentNames")) {
CardCollection differentNames = new CardCollection();
CardCollection discardMe = CardLists.filter(hand, CardPredicates.hasSVar("DiscardMe"));
while (c > 0) {
Card chosen;
if (!discardMe.isEmpty()) {
chosen = Aggregates.random(discardMe);
discardMe = CardLists.filter(discardMe, Predicates.not(CardPredicates.sharesNameWith(chosen)));
} else {
final Card worst = ComputerUtilCard.getWorstAI(hand);
chosen = worst != null ? worst : Aggregates.random(hand);
}
differentNames.add(chosen);
hand = CardLists.filter(hand, Predicates.not(CardPredicates.sharesNameWith(chosen)));
c--;
}
return PaymentDecision.card(differentNames);
}
else {
final AiController aic = ((PlayerControllerAi)player.getController()).getAi();
@@ -329,7 +350,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
}
CardCollectionView topLib = player.getCardsIn(ZoneType.Library, c);
return topLib.size() < c ? null : PaymentDecision.card(topLib);
return topLib.size() < c ? null : PaymentDecision.number(c);
}
@Override
@@ -494,7 +515,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override
public boolean apply(Card card) {
for (final SpellAbility sa : card.getSpellAbilities()) {
if (sa.isManaAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) {
return true;
}
}
@@ -579,7 +600,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override
public PaymentDecision visit(CostReveal cost) {
final String type = cost.getType();
CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
CardCollectionView hand = player.getCardsIn(cost.getRevealFrom());
if (cost.payCostFromSource()) {
if (!hand.contains(source)) {
@@ -627,41 +648,41 @@ public class AiCostDecision extends CostDecisionMakerBase {
// the first things are benefit from removing counters
// try to remove +1/+1 counter from undying creature
List<Card> prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.P1P1, c),
List<Card> prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.P1P1, c),
CardPredicates.hasKeyword("Undying"));
if (!prefs.isEmpty()) {
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.P1P1));
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.P1P1));
PaymentDecision result = PaymentDecision.card(prefs);
result.ct = CounterType.P1P1;
result.ct = CounterType.get(CounterEnumType.P1P1);
return result;
}
// try to remove -1/-1 counter from persist creature
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.M1M1, c),
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.M1M1, c),
CardPredicates.hasKeyword("Persist"));
if (!prefs.isEmpty()) {
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.M1M1));
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.M1M1));
PaymentDecision result = PaymentDecision.card(prefs);
result.ct = CounterType.M1M1;
result.ct = CounterType.get(CounterEnumType.M1M1);
return result;
}
// try to remove Time counter from Chronozoa, it will generate more
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.TIME, c),
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.TIME, c),
CardPredicates.nameEquals("Chronozoa"));
if (!prefs.isEmpty()) {
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.TIME));
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.TIME));
PaymentDecision result = PaymentDecision.card(prefs);
result.ct = CounterType.TIME;
result.ct = CounterType.get(CounterEnumType.TIME);
return result;
}
// try to remove Quest counter on something with enough counters for the
// effect to continue
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.QUEST, c));
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.QUEST, c));
if (!prefs.isEmpty()) {
prefs = CardLists.filter(prefs, new Predicate<Card>() {
@@ -673,12 +694,12 @@ public class AiCostDecision extends CostDecisionMakerBase {
if (crd.hasSVar("MaxQuestEffect")) {
e = Integer.parseInt(crd.getSVar("MaxQuestEffect"));
}
return crd.getCounters(CounterType.QUEST) >= e + c;
return crd.getCounters(CounterEnumType.QUEST) >= e + c;
}
});
Collections.sort(prefs, Collections.reverseOrder(CardPredicates.compareByCounterType(CounterType.QUEST)));
Collections.sort(prefs, Collections.reverseOrder(CardPredicates.compareByCounterType(CounterEnumType.QUEST)));
PaymentDecision result = PaymentDecision.card(prefs);
result.ct = CounterType.QUEST;
result.ct = CounterType.get(CounterEnumType.QUEST);
return result;
}
@@ -775,7 +796,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override
public boolean apply(final Card crd) {
for (Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey() == CounterType.valueOf(ctr))) {
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey().equals(CounterType.getType(ctr)))) {
return true;
}
}
@@ -787,7 +808,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
PaymentDecision result = PaymentDecision.card(card);
for (Map.Entry<CounterType, Integer> e : card.getCounters().entrySet()) {
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey() == CounterType.valueOf(ctr))) {
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey().equals(CounterType.getType(ctr)))) {
result.ct = e.getKey();
break;
}

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -65,7 +65,7 @@ import java.util.*;
* <p>
* ComputerUtil class.
* </p>
*
*
* @author Forge
* @version $Id$
*/
@@ -91,9 +91,6 @@ public class ComputerUtil {
}
}
source.setCastSA(sa);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
sa.setHostCard(game.getAction().moveToStack(source, sa));
}
@@ -109,7 +106,7 @@ public class ComputerUtil {
if (chooseTargets != null) {
chooseTargets.run();
}
if (sa.hasParam("Bestow")) {
if (sa.isBestow()) {
sa.getHostCard().animateBestow();
}
@@ -216,12 +213,9 @@ public class ComputerUtil {
sa.setActivatingPlayer(ai);
if (!ComputerUtilCost.canPayCost(sa, ai))
return false;
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
source.setCastSA(sa);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
sa.setHostCard(game.getAction().moveToStack(source, sa));
}
@@ -246,9 +240,6 @@ public class ComputerUtil {
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
source.setCastSA(sa);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
sa.setHostCard(game.getAction().moveToStack(source, sa));
}
@@ -267,9 +258,6 @@ public class ComputerUtil {
final Card source = newSA.getHostCard();
if (newSA.isSpell() && !source.isCopiedSpell()) {
source.setCastSA(newSA);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
newSA.setHostCard(game.getAction().moveToStack(source, sa));
if (newSA.getApi() == ApiType.Charm && !newSA.isWrapper()) {
@@ -290,9 +278,6 @@ public class ComputerUtil {
if (ComputerUtilCost.canPayCost(sa, ai)) {
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
source.setCastSA(sa);
sa.setLastStateBattlefield(game.getLastStateBattlefield());
sa.setLastStateGraveyard(game.getLastStateGraveyard());
sa.setHostCard(game.getAction().moveToStack(source, sa));
}
@@ -364,8 +349,8 @@ public class ComputerUtil {
for (int ip = 0; ip < 6; ip++) {
final int priority = 6 - ip;
if (priority == 2 && ai.isCardInPlay("Crucible of Worlds")) {
CardCollection landsInPlay = CardLists.getType(typeList, "Land");
if (!landsInPlay.isEmpty()) {
CardCollection landsInPlay = CardLists.getType(typeList, "Land");
if (!landsInPlay.isEmpty()) {
// Don't need more land.
return ComputerUtilCard.getWorstLand(landsInPlay);
}
@@ -394,16 +379,16 @@ public class ComputerUtil {
return ComputerUtilCard.getWorstLand(landsInPlay);
}
}
// try everything when about to die
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
if (!nonCreatures.isEmpty()) {
return ComputerUtilCard.getWorstAI(nonCreatures);
} else if (!typeList.isEmpty()) {
return ComputerUtilCard.getWorstAI(typeList);
}
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
if (!nonCreatures.isEmpty()) {
return ComputerUtilCard.getWorstAI(nonCreatures);
} else if (!typeList.isEmpty()) {
return ComputerUtilCard.getWorstAI(typeList);
}
}
}
else if (pref.contains("DiscardCost")) { // search for permanents with DiscardMe
@@ -465,14 +450,14 @@ public class ComputerUtil {
return ComputerUtilCard.getWorstLand(landsInHand);
}
}
// try everything when about to die
if (activate != null && "Reality Smasher".equals(activate.getName()) ||
game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
if (!typeList.isEmpty()) {
return ComputerUtilCard.getWorstAI(typeList);
}
game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
if (!typeList.isEmpty()) {
return ComputerUtilCard.getWorstAI(typeList);
}
}
} else if (pref.contains("DonateMe")) {
// search for permanents with DonateMe. priority 1 is the lowest, priority 5 the highest
@@ -555,7 +540,7 @@ public class ComputerUtil {
public static CardCollection chooseExileFrom(final Player ai, final ZoneType zone, final String type, final Card activate,
final Card target, final int amount) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null);
if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't exile the card we're pumping
}
@@ -576,7 +561,7 @@ public class ComputerUtil {
public static CardCollection choosePutToLibraryFrom(final Player ai, final ZoneType zone, final String type, final Card activate,
final Card target, final int amount) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null);
if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't move the card we're pumping
}
@@ -587,11 +572,11 @@ public class ComputerUtil {
CardLists.sortByPowerAsc(typeList);
final CardCollection list = new CardCollection();
if (zone != ZoneType.Hand) {
Collections.reverse(typeList);
}
for (int i = 0; i < amount; i++) {
list.add(typeList.get(i));
}
@@ -651,7 +636,7 @@ public class ComputerUtil {
}
ComputerUtilCard.sortByEvaluateCreature(typeList);
Collections.reverse(typeList);
final CardCollection tapList = new CardCollection();
// Accumulate from "worst" creature
@@ -724,7 +709,7 @@ public class ComputerUtil {
return returnList;
}
public static CardCollection choosePermanentsToSacrifice(final Player ai, final CardCollectionView cardlist, final int amount, final SpellAbility source,
public static CardCollection choosePermanentsToSacrifice(final Player ai, final CardCollectionView cardlist, final int amount, final SpellAbility source,
final boolean destroy, final boolean isOptional) {
CardCollection remaining = new CardCollection(cardlist);
final CardCollection sacrificed = new CardCollection();
@@ -733,9 +718,9 @@ public class ComputerUtil {
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold");
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
return sacrificed; // sacrifice none
}
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
return sacrificed; // sacrifice none
}
} else if ("DesecrationDemon".equals(source.getParam("AILogic"))) {
if (!SpecialCardAi.DesecrationDemon.considerSacrificingCreature(ai, source)) {
return sacrificed; // don't sacrifice unless in special conditions specified by DesecrationDemon AI
@@ -753,27 +738,27 @@ public class ComputerUtil {
boolean removedSelf = false;
if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit") || considerSacLogic) {
if (source.hasParam("Exploit")) {
for (Trigger t : host.getTriggers()) {
if (t.getMode() == TriggerType.Exploited) {
final String execute = t.getParam("Execute");
if (execute == null) {
continue;
}
final SpellAbility exSA = AbilityFactory.getAbility(host.getSVar(execute), host);
if (source.hasParam("Exploit")) {
for (Trigger t : host.getTriggers()) {
if (t.getMode() == TriggerType.Exploited) {
final String execute = t.getParam("Execute");
if (execute == null) {
continue;
}
final SpellAbility exSA = AbilityFactory.getAbility(host.getSVar(execute), host);
exSA.setActivatingPlayer(ai);
exSA.setTrigger(true);
exSA.setActivatingPlayer(ai);
exSA.setTrigger(true);
// Run non-mandatory trigger.
// These checks only work if the Executing SpellAbility is an Ability_Sub.
if ((exSA instanceof AbilitySub) && !SpellApiToAi.Converter.get(exSA.getApi()).doTriggerAI(ai, exSA, false)) {
// AI would not run this trigger if given the chance
return sacrificed;
}
}
}
}
// Run non-mandatory trigger.
// These checks only work if the Executing SpellAbility is an Ability_Sub.
if ((exSA instanceof AbilitySub) && !SpellApiToAi.Converter.get(exSA.getApi()).doTriggerAI(ai, exSA, false)) {
// AI would not run this trigger if given the chance
return sacrificed;
}
}
}
}
remaining = CardLists.filter(remaining, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -834,7 +819,7 @@ public class ComputerUtil {
if (ai.isOpponentOf(c.getController()))
return c;
}
if (destroy) {
final CardCollection indestructibles = CardLists.getKeyword(remaining, Keyword.INDESTRUCTIBLE);
if (!indestructibles.isEmpty()) {
@@ -924,7 +909,7 @@ public class ComputerUtil {
} catch (final Exception ex) {
throw new RuntimeException(TextUtil.concatNoSpace("There is an error in the card code for ", c.getName(), ":", ex.getMessage()), ex);
}
}
}
}
@@ -973,16 +958,16 @@ public class ComputerUtil {
final Card card = sa.getHostCard();
if (card.hasSVar("PlayMain1")) {
if (card.getSVar("PlayMain1").equals("ALWAYS") || sa.getPayCosts().hasNoManaCost()) {
return true;
} else if (card.getSVar("PlayMain1").equals("OPPONENTCREATURES")) {
//Only play these main1 when the opponent has creatures (stealing and giving them haste)
if (!ai.getOpponents().getCreaturesInPlay().isEmpty()) {
return true;
}
} else if (!card.getController().getCreaturesInPlay().isEmpty()) {
return true;
}
if (card.getSVar("PlayMain1").equals("ALWAYS") || sa.getPayCosts().hasNoManaCost()) {
return true;
} else if (card.getSVar("PlayMain1").equals("OPPONENTCREATURES")) {
//Only play these main1 when the opponent has creatures (stealing and giving them haste)
if (!ai.getOpponents().getCreaturesInPlay().isEmpty()) {
return true;
}
} else if (!card.getController().getCreaturesInPlay().isEmpty()) {
return true;
}
}
// try not to cast Raid creatures in main 1 if an attack is likely
@@ -995,7 +980,7 @@ public class ComputerUtil {
}
if (card.getManaCost().isZero()) {
return true;
return true;
}
if (card.hasKeyword(Keyword.RIOT) && ChooseGenericEffectAi.preferHasteForRiot(sa, ai)) {
@@ -1023,9 +1008,9 @@ public class ComputerUtil {
&& (card.hasKeyword(Keyword.HASTE) || ComputerUtil.hasACardGivingHaste(ai, true) || sa.isDash())) {
return true;
}
if (card.hasKeyword(Keyword.EXALTED)) {
return true;
return true;
}
//cast equipments in Main1 when there are creatures to equip and no other unequipped equipment
@@ -1155,7 +1140,7 @@ public class ComputerUtil {
if (discard.hasSVar("DiscardMe")) {
return true;
}
final Game game = ai.getGame();
final CardCollection landsInPlay = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS);
final CardCollection landsInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS);
@@ -1255,11 +1240,11 @@ public class ComputerUtil {
}
}
} // AntiBuffedBy
if (sub != null) {
if (sub != null) {
return castSpellInMain1(ai, sub);
}
return false;
}
@@ -1268,7 +1253,7 @@ public class ComputerUtil {
int activations = sa.getActivationsThisTurn();
if (!sa.isIntrinsic()) {
return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory
return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory
}
if (activations < 10) { //10 activations per turn should still be acceptable
@@ -1285,27 +1270,27 @@ public class ComputerUtil {
return false;
}
if (abCost.hasTapCost() && source.hasSVar("AITapDown")) {
return true;
return true;
} else if (sa.hasParam("Planeswalker") && ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostPutCounter) {
return true;
}
}
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostPutCounter) {
return true;
}
}
}
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostSacrifice) {
final CostSacrifice sac = (CostSacrifice) part;
final String type = sac.getType();
if (type.equals("CARDNAME")) {
if (source.getSVar("SacMe").equals("6")) {
return true;
}
continue;
}
final CardCollection typeList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(","), source.getController(), source, sa);
for (Card c : typeList) {
@@ -1340,14 +1325,14 @@ public class ComputerUtil {
Map<String, String> params = stAb.getMapParams();
if ("Continuous".equals(params.get("Mode")) && params.containsKey("AddKeyword")
&& params.get("AddKeyword").contains("Haste")) {
if (c.isEquipment() && c.getEquipping() == null) {
return true;
}
final String affected = params.get("Affected");
if (affected.contains("Creature.YouCtrl")
|| affected.contains("Other+YouCtrl")) {
|| affected.contains("Other+YouCtrl")) {
return true;
} else if (affected.contains("Creature.PairedWith") && !c.isPaired()) {
return true;
@@ -1356,10 +1341,10 @@ public class ComputerUtil {
}
for (Trigger t : c.getTriggers()) {
Map<String, String> params = t.getMapParams();
Map<String, String> params = t.getMapParams();
if (!"ChangesZone".equals(params.get("Mode"))
|| !"Battlefield".equals(params.get("Destination"))
|| !params.containsKey("ValidCard")) {
|| !"Battlefield".equals(params.get("Destination"))
|| !params.containsKey("ValidCard")) {
continue;
}
@@ -1367,7 +1352,7 @@ public class ComputerUtil {
if (valid.contains("Creature.YouCtrl")
|| valid.contains("Other+YouCtrl") ) {
final SpellAbility sa = t.getTriggeredSA();
final SpellAbility sa = t.getOverridingAbility();
if (sa != null && sa.getApi() == ApiType.Pump && sa.hasParam("KW")
&& sa.getParam("KW").contains("Haste")) {
return true;
@@ -1375,10 +1360,10 @@ public class ComputerUtil {
}
}
}
all.addAll(ai.getCardsActivableInExternalZones(true));
all.addAll(ai.getCardsIn(ZoneType.Hand));
for (final Card c : all) {
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.getApi() == ApiType.Pump && sa.hasParam("KW") && sa.getParam("KW").contains("Haste")) {
@@ -1413,10 +1398,10 @@ public class ComputerUtil {
public static boolean hasAFogEffect(final Player ai) {
final CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
all.addAll(ai.getCardsActivableInExternalZones(true));
all.addAll(ai.getCardsIn(ZoneType.Hand));
for (final Card c : all) {
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.getApi() != ApiType.Fog) {
@@ -1446,7 +1431,7 @@ public class ComputerUtil {
final CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
all.addAll(ai.getCardsActivableInExternalZones(true));
all.addAll(CardLists.filter(ai.getCardsIn(ZoneType.Hand), Predicates.not(Presets.PERMANENTS)));
for (final Card c : all) {
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.getApi() != ApiType.DealDamage) {
@@ -1505,7 +1490,7 @@ public class ComputerUtil {
/**
* Returns list of objects threatened by effects on the stack
*
*
* @param ai
* calling player
* @param sa
@@ -1520,7 +1505,7 @@ public class ComputerUtil {
if (game.getStack().isEmpty()) {
return objects;
}
// check stack for something that will kill this
for (SpellAbilityStackInstance si : game.getStack()) {
// iterate from top of stack to find SpellAbility, including sub-abilities,
@@ -1538,8 +1523,8 @@ public class ComputerUtil {
if (top) {
break; // only evaluate top-stack
}
}
}
return objects;
}
@@ -1551,14 +1536,14 @@ public class ComputerUtil {
int toughness = 0;
boolean grantIndestructible = false;
boolean grantShroud = false;
if (topStack == null) {
return objects;
}
final Card source = topStack.getHostCard();
final ApiType threatApi = topStack.getApi();
// Can only Predict things from AFs
if (threatApi == null) {
return threatened;
@@ -1572,7 +1557,7 @@ public class ComputerUtil {
CardCollectionView battleField = aiPlayer.getCardsIn(ZoneType.Battlefield);
objects = CardLists.getValidCards(battleField, topStack.getParam("ValidCards").split(","), source.getController(), source, topStack);
} else {
return threatened;
return threatened;
}
} else {
objects = topStack.getTargets().getTargets();
@@ -1586,7 +1571,7 @@ public class ComputerUtil {
}
}
if (canBeTargeted.isEmpty()) {
return threatened;
return threatened;
}
objects = canBeTargeted;
}
@@ -1655,7 +1640,7 @@ public class ComputerUtil {
}
// don't use it on creatures that can't be regenerated
if ((saviourApi == ApiType.Regenerate || saviourApi == ApiType.RegenerateAll) &&
if ((saviourApi == ApiType.Regenerate || saviourApi == ApiType.RegenerateAll) &&
(!c.canBeShielded() || noRegen)) {
continue;
}
@@ -1667,14 +1652,14 @@ public class ComputerUtil {
continue;
}
}
if (saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll) {
boolean canSave = ComputerUtilCombat.predictDamageTo(c, dmg - toughness, source, false) < ComputerUtilCombat.getDamageToKill(c);
if (!canSave) {
continue;
}
}
// cannot protect against source
if (saviourApi == ApiType.Protection && (ProtectAi.toProtectFrom(source, saviour) == null)) {
continue;
@@ -1685,7 +1670,7 @@ public class ComputerUtil {
if (saviourApi == ApiType.ChangeZone && (c.getOwner().isOpponentOf(aiPlayer) || c.isToken())) {
continue;
}
if (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c)) {
threatened.add(c);
}
@@ -1704,7 +1689,7 @@ public class ComputerUtil {
}
// -Toughness Curse
else if ((threatApi == ApiType.Pump || threatApi == ApiType.PumpAll && topStack.isCurse())
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|| saviourApi == ApiType.Protection || saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll
|| saviourApi == null)) {
final int dmg = -AbilityUtils.calculateAmount(topStack.getHostCard(),
@@ -1717,7 +1702,7 @@ public class ComputerUtil {
if (!canRemove) {
continue;
}
if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) {
final boolean cantSave = c.getNetToughness() + toughness <= dmg
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && !grantIndestructible
@@ -1726,14 +1711,14 @@ public class ComputerUtil {
continue;
}
}
if (saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll) {
boolean canSave = c.getNetToughness() + toughness > dmg;
if (!canSave) {
continue;
}
}
if (saviourApi == ApiType.Protection) {
if (tgt == null || (ProtectAi.toProtectFrom(source, saviour) == null)) {
continue;
@@ -1827,9 +1812,9 @@ public class ComputerUtil {
}
}
//GainControl
else if ((threatApi == ApiType.GainControl
|| (threatApi == ApiType.Attach && topStack.hasParam("AILogic") && topStack.getParam("AILogic").equals("GainControl") ))
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
else if ((threatApi == ApiType.GainControl
|| (threatApi == ApiType.Attach && topStack.hasParam("AILogic") && topStack.getParam("AILogic").equals("GainControl") ))
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|| saviourApi == ApiType.Protection || saviourApi == null)) {
for (final Object o : objects) {
if (o instanceof Card) {
@@ -1946,7 +1931,7 @@ public class ComputerUtil {
public static int scoreHand(CardCollectionView handList, Player ai, int cardsToReturn) {
// TODO Improve hand scoring in relation to cards to return.
// If final hand size is 5, score a hand based on what that 5 would be.
// Or if this is really really fast, determine what the 5 would be based on scoring
// Or if this is really really fast, determine what the 5 would be based on scoring
// All of the possibilities
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
@@ -2029,16 +2014,16 @@ public class ComputerUtil {
final CardCollectionView handList = ai.getCardsIn(ZoneType.Hand);
return scoreHand(handList, ai, cardsToReturn) <= 0;
}
public static CardCollection getPartialParisCandidates(Player ai) {
// Commander no longer uses partial paris.
final CardCollection candidates = new CardCollection();
final CardCollectionView handList = ai.getCardsIn(ZoneType.Hand);
final CardCollection lands = CardLists.getValidCards(handList, "Card.Land", ai, null);
final CardCollection nonLands = CardLists.getValidCards(handList, "Card.nonLand", ai, null);
CardLists.sortByCmcDesc(nonLands);
if (lands.size() >= 3 && lands.size() <= 4) {
return candidates;
}
@@ -2046,7 +2031,7 @@ public class ComputerUtil {
//Not enough lands!
int tgtCandidates = Math.max(Math.abs(lands.size()-nonLands.size()), 3);
System.out.println("Partial Paris: " + ai.getName() + " lacks lands, aiming to exile " + tgtCandidates + " cards.");
for (int i=0;i<tgtCandidates;i++) {
candidates.add(nonLands.get(i));
}
@@ -2068,7 +2053,7 @@ public class ComputerUtil {
numProducers.get(col).add(c);
}
}
}
}
}
}
@@ -2077,7 +2062,7 @@ public class ComputerUtil {
System.out.print(c.toString() + ", ");
}
System.out.println();
if (candidates.size() < 2) {
candidates.clear();
}
@@ -2117,7 +2102,7 @@ public class ComputerUtil {
CardCollection landsInHand = CardLists.filter(cardsInHand, CardPredicates.Presets.LANDS_PRODUCING_MANA);
// valuable mana-producing artifacts that may be equated to a land
List<String> manaArts = Arrays.asList("Mox Pearl", "Mox Sapphire", "Mox Jet", "Mox Ruby", "Mox Emerald");
// evaluate creatures available in deck
CardCollectionView allCreatures = CardLists.filter(allCards, Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.isOwner(player)));
int numCards = allCreatures.size();
@@ -2200,7 +2185,7 @@ public class ComputerUtil {
}
Collections.sort(goodChoices, CardLists.TextLenComparator);
CardLists.sortByCmcDesc(goodChoices);
dChoices.add(goodChoices.get(0));
@@ -2211,15 +2196,18 @@ public class ComputerUtil {
if (p == aiChooser) { // ask that ai player what he would like to discard
final AiController aic = ((PlayerControllerAi)p.getController()).getAi();
return aic.getCardsToDiscard(min, max, validCards, sa);
}
}
// no special options for human or remote friends
return getCardsToDiscardFromOpponent(aiChooser, p, sa, validCards, min, max);
}
public static String chooseSomeType(Player ai, String kindOfType, String logic, List<String> invalidTypes) {
public static String chooseSomeType(Player ai, String kindOfType, String logic, Collection<String> validTypes, List<String> invalidTypes) {
if (invalidTypes == null) {
invalidTypes = ImmutableList.of();
}
if (validTypes == null) {
validTypes = ImmutableList.of();
}
final Game game = ai.getGame();
String chosen = "";
@@ -2243,7 +2231,7 @@ public class ComputerUtil {
}
}
if (StringUtils.isEmpty(chosen)) {
chosen = "Creature";
chosen = validTypes.isEmpty() ? "Creature" : Aggregates.random(validTypes);
}
} else if (kindOfType.equals("Creature")) {
if (logic != null) {
@@ -2257,7 +2245,7 @@ public class ComputerUtil {
chosen = ComputerUtilCard.getMostProminentType(ai.getCardsIn(ZoneType.Battlefield), valid);
}
else if (logic.equals("MostProminentOppControls")) {
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
chosen = ComputerUtilCard.getMostProminentType(list, valid);
if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) {
list = CardLists.filterControlledBy(game.getCardsInGame(), ai.getOpponents());
@@ -2284,11 +2272,11 @@ public class ComputerUtil {
chosen = ComputerUtilCard.getMostProminentType(list, valid);
} else if (logic.equals("MostNeededType")) {
// Choose a type that is in the deck, but not in hand or on the battlefield
// Choose a type that is in the deck, but not in hand or on the battlefield
final List<String> basics = new ArrayList<>(CardType.Constant.BASIC_TYPES);
CardCollectionView presentCards = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand));
CardCollectionView possibleCards = ai.getAllCards();
for (String b : basics) {
if (!Iterables.any(presentCards, CardPredicates.isType(b)) && Iterables.any(possibleCards, CardPredicates.isType(b))) {
chosen = b;
@@ -2338,13 +2326,15 @@ public class ComputerUtil {
return chosen;
}
public static Object vote(Player ai, List<Object> options, SpellAbility sa, Multimap<Object, Player> votes) {
public static Object vote(Player ai, List<Object> options, SpellAbility sa, Multimap<Object, Player> votes, Player forPlayer) {
final Card source = sa.getHostCard();
final Player controller = source.getController();
final Game game = controller.getGame();
boolean opponent = controller.isOpponentOf(ai);
final CounterType p1p1Type = CounterType.get(CounterEnumType.P1P1);
if (!sa.hasParam("AILogic")) {
return Aggregates.random(options);
}
@@ -2398,7 +2388,7 @@ public class ComputerUtil {
}
}
// is it can't receive counters, choose +1/+1 ones
if (!source.canReceiveCounters(CounterType.P1P1)) {
if (!source.canReceiveCounters(p1p1Type)) {
return opponent ? "Feather" : "Quill";
}
// if source is not on the battlefield anymore, choose +1/+1
@@ -2430,7 +2420,7 @@ public class ComputerUtil {
Card token = TokenAi.spawnToken(controller, saToken);
// is it can't receive counters, choose +1/+1 ones
if (!source.canReceiveCounters(CounterType.P1P1)) {
if (!source.canReceiveCounters(p1p1Type)) {
return opponent ? "Strength" : "Numbers";
}
@@ -2440,7 +2430,7 @@ public class ComputerUtil {
}
// token would not survive
if (token == null) {
if (token == null || !token.isCreature() || token.getNetToughness() < 1) {
return opponent ? "Numbers" : "Strength";
}
@@ -2453,11 +2443,11 @@ public class ComputerUtil {
Card sourceNumbers = CardUtil.getLKICopy(source);
Card sourceStrength = CardUtil.getLKICopy(source);
sourceNumbers.setCounters(CounterType.P1P1, sourceNumbers.getCounters(CounterType.P1P1) + numStrength);
sourceNumbers.setCounters(p1p1Type, sourceNumbers.getCounters(p1p1Type) + numStrength);
sourceNumbers.setZone(source.getZone());
sourceStrength.setCounters(CounterType.P1P1,
sourceStrength.getCounters(CounterType.P1P1) + numStrength + 1);
sourceStrength.setCounters(p1p1Type,
sourceStrength.getCounters(p1p1Type) + numStrength + 1);
sourceStrength.setZone(source.getZone());
int scoreStrength = ComputerUtilCard.evaluateCreature(sourceStrength) + tokenScore * numNumbers;
@@ -2479,7 +2469,7 @@ public class ComputerUtil {
}
// is it can't receive counters, choose +1/+1 ones
if (!source.canReceiveCounters(CounterType.P1P1)) {
if (!source.canReceiveCounters(p1p1Type)) {
return opponent ? "Sprout" : "Harvest";
}
@@ -2556,11 +2546,11 @@ public class ComputerUtil {
});
return ComputerUtilCard.getBestCreatureAI(killables);
}
public static int predictDamageFromSpell(final SpellAbility sa, final Player targetPlayer) {
int damage = -1; // returns -1 if the spell does not deal damage
final Card card = sa.getHostCard();
SpellAbility ab = sa;
while (ab != null) {
if (ab.getApi() == ApiType.DealDamage) {
@@ -2579,12 +2569,12 @@ public class ComputerUtil {
}
ab = ab.getSubAbility();
}
return damage;
}
public static int getDamageForPlaying(final Player player, final SpellAbility sa) {
// check for bad spell cast triggers
int damage = 0;
final Game game = player.getGame();
@@ -2614,7 +2604,7 @@ public class ComputerUtil {
continue;
}
}
if (trigParams.containsKey("ValidActivatingPlayer")) {
if (!player.isValid(trigParams.get("ValidActivatingPlayer"), source.getController(), source, sa)) {
continue;
@@ -2674,7 +2664,7 @@ public class ComputerUtil {
}
}
}
return damage;
}
@@ -2697,7 +2687,7 @@ public class ComputerUtil {
if (!trigger.requirementsCheck(game)) {
continue;
}
if (trigParams.containsKey("CheckOnTriggeredCard")
if (trigParams.containsKey("CheckOnTriggeredCard")
&& AbilityUtils.getDefinedCards(permanent, source.getSVar(trigParams.get("CheckOnTriggeredCard").split(" ")[0]), null).isEmpty()) {
continue;
}
@@ -2771,27 +2761,27 @@ public class ComputerUtil {
}
public static boolean isNegativeCounter(CounterType type, Card c) {
return type == CounterType.AGE || type == CounterType.BRIBERY || type == CounterType.DOOM
|| type == CounterType.M1M1 || type == CounterType.M0M2 || type == CounterType.M0M1
|| type == CounterType.M1M0 || type == CounterType.M2M1 || type == CounterType.M2M2
return type.is(CounterEnumType.AGE) || type.is(CounterEnumType.BRIBERY) || type.is(CounterEnumType.DOOM)
|| type.is(CounterEnumType.M1M1) || type.is(CounterEnumType.M0M2) || type.is(CounterEnumType.M0M1)
|| type.is(CounterEnumType.M1M0) || type.is(CounterEnumType.M2M1) || type.is(CounterEnumType.M2M2)
// Blaze only hurts Lands
|| (type == CounterType.BLAZE && c.isLand())
|| (type.is(CounterEnumType.BLAZE) && c.isLand())
// Iceberg does use Ice as Storage
|| (type == CounterType.ICE && !"Iceberg".equals(c.getName()))
|| (type.is(CounterEnumType.ICE) && !"Iceberg".equals(c.getName()))
// some lands does use Depletion as Storage Counter
|| (type == CounterType.DEPLETION && c.hasKeyword("CARDNAME doesn't untap during your untap step."))
|| (type.is(CounterEnumType.DEPLETION) && c.hasKeyword("CARDNAME doesn't untap during your untap step."))
// treat Time Counters on suspended Cards as Bad,
// and also on Chronozoa
|| (type == CounterType.TIME && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|| type == CounterType.GOLD || type == CounterType.MUSIC || type == CounterType.PUPA
|| type == CounterType.PARALYZATION || type == CounterType.SHELL || type == CounterType.SLEEP
|| type == CounterType.SLUMBER || type == CounterType.SLEIGHT || type == CounterType.WAGE;
|| (type.is(CounterEnumType.TIME) && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|| type.is(CounterEnumType.GOLD) || type.is(CounterEnumType.MUSIC) || type.is(CounterEnumType.PUPA)
|| type.is(CounterEnumType.PARALYZATION) || type.is(CounterEnumType.SHELL) || type.is(CounterEnumType.SLEEP)
|| type.is(CounterEnumType.SLUMBER) || type.is(CounterEnumType.SLEIGHT) || type.is(CounterEnumType.WAGE);
}
// this countertypes has no effect
public static boolean isUselessCounter(CounterType type) {
return type == CounterType.AWAKENING || type == CounterType.MANIFESTATION || type == CounterType.PETRIFICATION
|| type == CounterType.TRAINING;
return type.is(CounterEnumType.AWAKENING) || type.is(CounterEnumType.MANIFESTATION) || type.is(CounterEnumType.PETRIFICATION)
|| type.is(CounterEnumType.TRAINING);
}
public static Player evaluateBoardPosition(final List<Player> listToEvaluate) {
@@ -2903,7 +2893,7 @@ public class ComputerUtil {
return false;
}
public static boolean targetPlayableSpellCard(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost) {
// determine and target a card with a SA that the AI can afford and will play
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();

View File

@@ -82,6 +82,10 @@ public class ComputerUtilAbility {
final List<SpellAbility> spellAbilities = Lists.newArrayList();
for (final Card c : l) {
for (final SpellAbility sa : c.getSpellAbilities()) {
// Spells of permanents can't be activated on the battlefield
if (c.isPermanent() && sa.isSpell() && c.isInZone(ZoneType.Battlefield)) {
continue;
}
spellAbilities.add(sa);
}
if (c.isFaceDown() && c.isInZone(ZoneType.Exile) && !c.mayPlay(player).isEmpty()) {
@@ -109,9 +113,7 @@ public class ComputerUtilAbility {
List<SpellAbility> priorityAltSa = Lists.newArrayList();
List<SpellAbility> otherAltSa = Lists.newArrayList();
for (SpellAbility altSa : saAltCosts) {
if (altSa.getPayCosts() == null || sa.getPayCosts() == null) {
otherAltSa.add(altSa);
} else if (sa.getPayCosts().isOnlyManaCost()
if (sa.getPayCosts().isOnlyManaCost()
&& altSa.getPayCosts().isOnlyManaCost() && sa.getPayCosts().getTotalMana().compareTo(altSa.getPayCosts().getTotalMana()) == 1) {
// the alternative cost is strictly cheaper, so why not? (e.g. Omniscience etc.)
priorityAltSa.add(altSa);

View File

@@ -5,7 +5,6 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.card.CardType;
import forge.card.ColorSet;
import forge.card.MagicColor;
@@ -172,30 +171,30 @@ public class ComputerUtilCard {
// if no non-basic lands, target the least represented basic land type
String sminBL = "";
int iminBL = 20000; // hopefully no one will ever have more than 20000
// lands of one type....
int iminBL = Integer.MAX_VALUE;
int n = 0;
for (String name : MagicColor.Constant.BASIC_LANDS) {
n = CardLists.getType(land, name).size();
if ((n < iminBL) && (n > 0)) {
// if two or more are tied, only the
// first
// one checked will be used
if (n < iminBL && n > 0) {
iminBL = n;
sminBL = name;
}
}
if (iminBL == 20000) {
return null; // no basic land was a minimum
if (iminBL == Integer.MAX_VALUE) {
// All basic lands have no basic land type. Just return something
Iterator<Card> untapped = Iterables.filter(land, CardPredicates.Presets.UNTAPPED).iterator();
if (untapped.hasNext()) {
return untapped.next();
}
return land.get(0);
}
final List<Card> bLand = CardLists.getType(land, sminBL);
for (Card ut : Iterables.filter(bLand, CardPredicates.Presets.UNTAPPED)) {
return ut;
}
return Aggregates.random(bLand); // random tapped land of least represented type
}
@@ -643,7 +642,7 @@ public class ComputerUtilCard {
return getMostProminentType(list, CardType.getAllCreatureTypes());
}
public static String getMostProminentType(final CardCollectionView list, final List<String> valid) {
public static String getMostProminentType(final CardCollectionView list, final Collection<String> valid) {
if (list.size() == 0) {
return "";
}
@@ -964,6 +963,22 @@ public class ComputerUtilCard {
}
chosen.add(chosenColor);
}
else if (logic.equals("HighestDevotionToColor")) {
int curDevotion = 0;
String chosenColor = MagicColor.Constant.WHITE;
CardCollectionView hand = ai.getCardsIn(ZoneType.Hand);
for(byte c : MagicColor.WUBRG) {
String devotionCode = "Count$Devotion." + MagicColor.toLongString(c);
int devotion = CardFactoryUtil.xCount(sa.getHostCard(), devotionCode);
if (devotion > curDevotion && !CardLists.filter(hand, CardPredicates.isColor(c)).isEmpty()) {
curDevotion = devotion;
chosenColor = MagicColor.toLongString(c);
}
}
chosen.add(chosenColor);
}
}
if (chosen.isEmpty()) {
chosen.add(MagicColor.Constant.GREEN);
@@ -1407,8 +1422,8 @@ public class ComputerUtilCard {
if (combat.isAttacking(c) && opp.getLife() > 0) {
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opp, combat, true);
int pumpedDmg = ComputerUtilCombat.damageIfUnblocked(pumped, opp, pumpedCombat, true);
int poisonOrig = opp.canReceiveCounters(CounterType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(c, ai) : 0;
int poisonPumped = opp.canReceiveCounters(CounterType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0;
int poisonOrig = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(c, ai) : 0;
int poisonPumped = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0;
// predict Infect
if (pumpedDmg == 0 && c.hasKeyword(Keyword.INFECT)) {
@@ -1431,7 +1446,8 @@ public class ComputerUtilCard {
}
if (pumpedDmg > dmg) {
if ((!c.hasKeyword(Keyword.INFECT) && pumpedDmg >= opp.getLife())
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())) {
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterEnumType.POISON) && pumpedDmg >= opp.getPoisonCounters())
|| ("PumpForTrample".equals(sa.getParam("AILogic")))) {
return true;
}
}
@@ -1458,7 +1474,7 @@ public class ComputerUtilCard {
if (totalPowerUnblocked >= opp.getLife()) {
return true;
} else if (totalPowerUnblocked > dmg && sa.getHostCard() != null && sa.getHostCard().isInPlay()) {
if (sa.getPayCosts() != null && sa.getPayCosts().hasNoManaCost()) {
if (sa.getPayCosts().hasNoManaCost()) {
return true; // always activate abilities which cost no mana and which can increase unblocked damage
}
}
@@ -1749,10 +1765,10 @@ public class ComputerUtilCard {
}
public static boolean hasActiveUndyingOrPersist(final Card c) {
if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterType.P1P1) == 0) {
if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterEnumType.P1P1) == 0) {
return true;
}
if (c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterType.M1M1) == 0) {
if (c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterEnumType.M1M1) == 0) {
return true;
}
return false;
@@ -1769,10 +1785,6 @@ public class ComputerUtilCard {
for (Card c : otb) {
for (SpellAbility sa : c.getSpellAbilities()) {
if (sa.getPayCosts() == null) {
continue;
}
CostPayEnergy energyCost = sa.getPayCosts().getCostEnergy();
if (energyCost != null) {
int amount = energyCost.convertAmount();
@@ -1896,21 +1908,12 @@ public class ComputerUtilCard {
}
if (card.getSVar(needsToPlayVarName).length() > 0) {
final String needsToPlay = card.getSVar(needsToPlayVarName);
int x = 0;
int y = 0;
String sVar = needsToPlay.split(" ")[0];
String comparator = needsToPlay.split(" ")[1];
String compareTo = comparator.substring(2);
try {
x = Integer.parseInt(sVar);
} catch (final NumberFormatException e) {
x = CardFactoryUtil.xCount(card, card.getSVar(sVar));
}
try {
y = Integer.parseInt(compareTo);
} catch (final NumberFormatException e) {
y = CardFactoryUtil.xCount(card, card.getSVar(compareTo));
}
int x = AbilityUtils.calculateAmount(card, sVar, sa);
int y = AbilityUtils.calculateAmount(card, compareTo, sa);
if (!Expressions.compare(x, comparator, y)) {
return AiPlayDecision.NeedsToPlayCriteriaNotMet;
}

View File

@@ -328,7 +328,7 @@ public class ComputerUtilCombat {
public static int resultingPoison(final Player ai, final Combat combat) {
// ai can't get poision counters, so the value can't change
if (!ai.canReceiveCounters(CounterType.POISON)) {
if (!ai.canReceiveCounters(CounterEnumType.POISON)) {
return ai.getPoisonCounters();
}
@@ -931,7 +931,7 @@ public class ComputerUtilCombat {
if (dealsFirstStrikeDamage(attacker, withoutAbilities, null)
&& (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))
&& !dealsFirstStrikeDamage(blocker, withoutAbilities, null)
&& !blocker.canReceiveCounters(CounterType.M1M1)) {
&& !blocker.canReceiveCounters(CounterEnumType.M1M1)) {
power -= attacker.getNetCombatDamage();
}
@@ -973,62 +973,45 @@ public class ComputerUtilCombat {
}
theTriggers.addAll(attacker.getTriggers());
for (final Trigger trigger : theTriggers) {
final Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
continue;
}
Map<String, String> abilityParams = null;
if (trigger.getOverridingAbility() != null) {
abilityParams = trigger.getOverridingAbility().getMapParams();
} else if (trigParams.containsKey("Execute")) {
final String ability = source.getSVar(trigParams.get("Execute"));
abilityParams = AbilityFactory.getMapParams(ability);
} else {
SpellAbility sa = trigger.ensureAbility();
if (sa == null) {
continue;
}
if (abilityParams.containsKey("AB") && !abilityParams.get("AB").equals("Pump")) {
if (!ApiType.Pump.equals(sa.getApi())) {
continue;
}
if (abilityParams.containsKey("DB") && !abilityParams.get("DB").equals("Pump")) {
if (sa.usesTargeting()) {
continue;
}
if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) {
continue; // targeted pumping not supported
if (!sa.hasParam("NumAtt")) {
continue;
}
final List<Card> list = AbilityUtils.getDefinedCards(source, abilityParams.get("Defined"), null);
if (abilityParams.containsKey("Defined") && abilityParams.get("Defined").equals("TriggeredBlocker")) {
String defined = sa.getParam("Defined");
final List<Card> list = AbilityUtils.getDefinedCards(source, defined, sa);
if ("TriggeredBlocker".equals(defined)) {
list.add(blocker);
}
if (list.isEmpty()) {
continue;
}
if (!list.contains(blocker)) {
continue;
}
if (!abilityParams.containsKey("NumAtt")) {
continue;
}
String att = abilityParams.get("NumAtt");
if (att.startsWith("+")) {
att = att.substring(1);
}
try {
power += Integer.parseInt(att);
} catch (final NumberFormatException nfe) {
// can't parse the number (X for example)
power += 0;
}
power += AbilityUtils.calculateAmount(source, sa.getParam("NumAtt"), sa, true);
}
if (withoutAbilities) {
return power;
}
for (SpellAbility ability : blocker.getAllSpellAbilities()) {
if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) {
if (!(ability instanceof AbilityActivated)) {
continue;
}
if (ability.hasParam("ActivationPhases") || ability.hasParam("SorcerySpeed") || ability.hasParam("ActivationZone")) {
@@ -1058,7 +1041,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && blocker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && blocker.getCounters(CounterEnumType.P1P1) > 0) {
continue;
}
@@ -1108,102 +1091,61 @@ public class ComputerUtilCombat {
}
theTriggers.addAll(attacker.getTriggers());
for (final Trigger trigger : theTriggers) {
final Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) {
continue;
}
Map<String, String> abilityParams = null;
if (trigger.getOverridingAbility() != null) {
abilityParams = trigger.getOverridingAbility().getMapParams();
} else if (trigParams.containsKey("Execute")) {
final String ability = source.getSVar(trigParams.get("Execute"));
abilityParams = AbilityFactory.getMapParams(ability);
} else {
SpellAbility sa = trigger.ensureAbility();
if (sa == null) {
continue;
}
String abType = "";
if (abilityParams.containsKey("AB")) {
abType = abilityParams.get("AB");
} else if (abilityParams.containsKey("DB")) {
abType = abilityParams.get("DB");
}
// DealDamage triggers
if (abType.equals("DealDamage")) {
if (!abilityParams.containsKey("Defined") || !abilityParams.get("Defined").equals("TriggeredBlocker")) {
continue;
}
int damage = 0;
try {
damage = Integer.parseInt(abilityParams.get("NumDmg"));
} catch (final NumberFormatException nfe) {
// can't parse the number (X for example)
if (ApiType.DealDamage.equals(sa.getApi())) {
if (!"TriggeredBlocker".equals(sa.getParam("Defined"))) {
continue;
}
int damage = AbilityUtils.calculateAmount(source, sa.getParam("NumDmg"), sa);
toughness -= predictDamageTo(blocker, damage, 0, source, false);
continue;
}
} else
// -1/-1 PutCounter triggers
if (abType.equals("PutCounter")) {
if (!abilityParams.containsKey("Defined") || !abilityParams.get("Defined").equals("TriggeredBlocker")) {
if (ApiType.PutCounter.equals(sa.getApi())) {
if (!"TriggeredBlocker".equals(sa.getParam("Defined"))) {
continue;
}
if (!abilityParams.containsKey("CounterType") || !abilityParams.get("CounterType").equals("M1M1")) {
if (!"M1M1".equals(sa.getParam("CounterType"))) {
continue;
}
int num = 0;
try {
num = Integer.parseInt(abilityParams.get("CounterNum"));
} catch (final NumberFormatException nfe) {
// can't parse the number (X for example)
continue;
}
toughness -= num;
continue;
}
toughness -= AbilityUtils.calculateAmount(source, sa.getParam("CounterNum"), sa);
} else
// Pump triggers
if (!abType.equals("Pump")) {
continue;
}
if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) {
continue; // targeted pumping not supported
}
final List<Card> list = AbilityUtils.getDefinedCards(source, abilityParams.get("Defined"), null);
if (abilityParams.containsKey("Defined") && abilityParams.get("Defined").equals("TriggeredBlocker")) {
list.add(blocker);
}
if (list.isEmpty()) {
continue;
}
if (!list.contains(blocker)) {
continue;
}
if (!abilityParams.containsKey("NumDef")) {
continue;
}
String def = abilityParams.get("NumDef");
if (def.startsWith("+")) {
def = def.substring(1);
}
try {
toughness += Integer.parseInt(def);
} catch (final NumberFormatException nfe) {
// can't parse the number (X for example)
if (ApiType.Pump.equals(sa.getApi())) {
if (sa.usesTargeting()) {
continue; // targeted pumping not supported
}
final List<Card> list = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), null);
if ("TriggeredBlocker".equals(sa.getParam("Defined"))) {
list.add(blocker);
}
if (list.isEmpty() || !list.contains(blocker)) {
continue;
}
if (!sa.hasParam("NumDef")) {
continue;
}
toughness += AbilityUtils.calculateAmount(source, sa.getParam("NumDef"), sa, true);
}
}
if (withoutAbilities) {
return toughness;
}
for (SpellAbility ability : blocker.getAllSpellAbilities()) {
if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) {
if (!(ability instanceof AbilityActivated)) {
continue;
}
@@ -1234,7 +1176,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && blocker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && blocker.getCounters(CounterEnumType.P1P1) > 0) {
continue;
}
@@ -1296,7 +1238,7 @@ public class ComputerUtilCombat {
if (ComputerUtilCombat.dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
&& (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT))
&& !ComputerUtilCombat.dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
&& !attacker.canReceiveCounters(CounterType.M1M1)) {
&& !attacker.canReceiveCounters(CounterEnumType.M1M1)) {
power -= blocker.getNetCombatDamage();
}
theTriggers.addAll(blocker.getTriggers());
@@ -1426,7 +1368,7 @@ public class ComputerUtilCombat {
return power;
}
for (SpellAbility ability : attacker.getAllSpellAbilities()) {
if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) {
if (!(ability instanceof AbilityActivated)) {
continue;
}
if (ability.hasParam("ActivationPhases") || ability.hasParam("SorcerySpeed") || ability.hasParam("ActivationZone")) {
@@ -1456,7 +1398,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && attacker.getCounters(CounterEnumType.P1P1) > 0) {
continue;
}
@@ -1521,148 +1463,135 @@ public class ComputerUtilCombat {
final CardCollectionView cardList = game.getCardsIn(ZoneType.Battlefield);
for (final Card card : cardList) {
for (final StaticAbility stAb : card.getStaticAbilities()) {
final Map<String, String> params = stAb.getMapParams();
if (!params.get("Mode").equals("Continuous")) {
if (!"Continuous".equals(stAb.getParam("Mode"))) {
continue;
}
if (params.containsKey("Affected") && params.get("Affected").contains("attacking")) {
final String valid = TextUtil.fastReplace(params.get("Affected"), "attacking", "Creature");
if (!stAb.hasParam("Affected")) {
continue;
}
if (!stAb.hasParam("AddToughness")) {
continue;
}
String affected = stAb.getParam("Affected");
String addT = stAb.getParam("AddToughness");
if (affected.contains("attacking")) {
final String valid = TextUtil.fastReplace(affected, "attacking", "Creature");
if (!attacker.isValid(valid, card.getController(), card, null)) {
continue;
}
if (params.containsKey("AddToughness")) {
if (params.get("AddToughness").equals("X")) {
toughness += CardFactoryUtil.xCount(card, card.getSVar("X"));
} else if (params.get("AddToughness").equals("Y")) {
toughness += CardFactoryUtil.xCount(card, card.getSVar("Y"));
} else {
toughness += Integer.valueOf(params.get("AddToughness"));
}
}
} else if (params.containsKey("Affected") && params.get("Affected").contains("untapped")) {
final String valid = TextUtil.fastReplace(params.get("Affected"), "untapped", "Creature");
toughness += AbilityUtils.calculateAmount(card, addT, stAb, true);
} else if (affected.contains("untapped")) {
final String valid = TextUtil.fastReplace(affected, "untapped", "Creature");
if (!attacker.isValid(valid, card.getController(), card, null)
|| attacker.hasKeyword(Keyword.VIGILANCE)) {
continue;
}
// remove the bonus, because it will no longer be granted
if (params.containsKey("AddToughness")) {
toughness -= Integer.valueOf(params.get("AddToughness"));
}
toughness -= AbilityUtils.calculateAmount(card, addT, stAb, true);
}
}
}
}
for (final Trigger trigger : theTriggers) {
final Map<String, String> trigParams = trigger.getMapParams();
final Card source = trigger.getHostCard();
if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)) {
continue;
}
Map<String, String> abilityParams = null;
if (trigger.getOverridingAbility() != null) {
abilityParams = trigger.getOverridingAbility().getMapParams();
} else if (trigParams.containsKey("Execute")) {
final String ability = source.getSVar(trigParams.get("Execute"));
abilityParams = AbilityFactory.getMapParams(ability);
} else {
SpellAbility sa = trigger.ensureAbility();
if (sa == null) {
continue;
}
sa.setActivatingPlayer(source.getController());
if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) {
if (sa.usesTargeting()) {
continue; // targeted pumping not supported
}
// DealDamage triggers
if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("DealDamage"))
|| (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("DealDamage"))) {
if (!abilityParams.containsKey("Defined") || !abilityParams.get("Defined").equals("TriggeredAttacker")) {
continue;
}
int damage = 0;
try {
damage = Integer.parseInt(abilityParams.get("NumDmg"));
} catch (final NumberFormatException nfe) {
// can't parse the number (X for example)
if (ApiType.DealDamage.equals(sa.getApi())) {
if ("TriggeredAttacker".equals(sa.getParam("Defined"))) {
continue;
}
int damage = AbilityUtils.calculateAmount(source, sa.getParam("NumDmg"), sa);
toughness -= predictDamageTo(attacker, damage, 0, source, false);
continue;
}
} else if (ApiType.Pump.equals(sa.getApi())) {
// Pump triggers
if (abilityParams.containsKey("AB") && !abilityParams.get("AB").equals("Pump")
&& !abilityParams.get("AB").equals("PumpAll")) {
continue;
}
if (abilityParams.containsKey("DB") && !abilityParams.get("DB").equals("Pump")
&& !abilityParams.get("DB").equals("PumpAll")) {
continue;
}
if (abilityParams.containsKey("Cost")) {
SpellAbility sa = null;
if (trigger.getOverridingAbility() != null) {
sa = trigger.getOverridingAbility();
} else {
final String ability = source.getSVar(trigParams.get("Execute"));
sa = AbilityFactory.getAbility(ability, source);
if (sa.hasParam("Cost")) {
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
continue;
}
}
sa.setActivatingPlayer(source.getController());
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
if (!sa.hasParam("NumDef")) {
continue;
}
}
List<Card> list = Lists.newArrayList();
if (!abilityParams.containsKey("ValidCards")) {
list = AbilityUtils.getDefinedCards(source, abilityParams.get("Defined"), null);
}
if (abilityParams.containsKey("Defined") && abilityParams.get("Defined").equals("TriggeredAttacker")) {
list.add(attacker);
}
if (abilityParams.containsKey("ValidCards")) {
if (attacker.isValid(abilityParams.get("ValidCards").split(","), source.getController(), source, null)
|| attacker.isValid(abilityParams.get("ValidCards").replace("attacking+", "").split(","),
source.getController(), source, null)) {
CardCollection list = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
if ("TriggeredAttacker".equals(sa.getParam("Defined"))) {
list.add(attacker);
}
}
if (list.isEmpty()) {
continue;
}
if (!list.contains(attacker)) {
continue;
}
if (!abilityParams.containsKey("NumDef")) {
continue;
}
String def = abilityParams.get("NumDef");
if (def.startsWith("+")) {
def = def.substring(1);
}
if (def.matches("[0-9][0-9]?") || def.matches("-" + "[0-9][0-9]?")) {
toughness += Integer.parseInt(def);
} else {
String bonus = source.getSVar(def);
if (bonus.contains("TriggerCount$NumBlockers")) {
bonus = TextUtil.fastReplace(bonus, "TriggerCount$NumBlockers", "Number$1");
} else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee
bonus = TextUtil.fastReplace(bonus, "TriggeredPlayersDefenders$Amount", "Number$1");
if (!list.contains(attacker)) {
continue;
}
String def = sa.getParam("NumDef");
if (def.startsWith("+")) {
def = def.substring(1);
}
if (def.matches("[0-9][0-9]?") || def.matches("-" + "[0-9][0-9]?")) {
toughness += Integer.parseInt(def);
} else {
String bonus = AbilityUtils.getSVar(sa, def);
if (bonus.contains("TriggerCount$NumBlockers")) {
bonus = TextUtil.fastReplace(bonus, "TriggerCount$NumBlockers", "Number$1");
} else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee
bonus = TextUtil.fastReplace(bonus, "TriggeredPlayersDefenders$Amount", "Number$1");
}
toughness += CardFactoryUtil.xCount(source, bonus);
}
} else if (ApiType.PumpAll.equals(sa.getApi())) {
if (sa.hasParam("Cost")) {
if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) {
continue;
}
}
if (!sa.hasParam("ValidCards")) {
continue;
}
if (!sa.hasParam("NumDef")) {
continue;
}
if (!attacker.isValid(sa.getParam("ValidCards").replace("attacking+", "").split(","), source.getController(), source, sa)) {
continue;
}
String def = sa.getParam("NumDef");
if (def.startsWith("+")) {
def = def.substring(1);
}
if (def.matches("[0-9][0-9]?") || def.matches("-" + "[0-9][0-9]?")) {
toughness += Integer.parseInt(def);
} else {
String bonus = AbilityUtils.getSVar(sa, def);
if (bonus.contains("TriggerCount$NumBlockers")) {
bonus = TextUtil.fastReplace(bonus, "TriggerCount$NumBlockers", "Number$1");
} else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee
bonus = TextUtil.fastReplace(bonus, "TriggeredPlayersDefenders$Amount", "Number$1");
}
toughness += CardFactoryUtil.xCount(source, bonus);
}
toughness += CardFactoryUtil.xCount(source, bonus);
}
}
if (withoutAbilities) {
return toughness;
}
for (SpellAbility ability : attacker.getAllSpellAbilities()) {
if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) {
if (!(ability instanceof AbilityActivated)) {
continue;
}
@@ -1672,18 +1601,19 @@ public class ComputerUtilCombat {
if (ability.usesTargeting() && !ability.canTarget(attacker)) {
continue;
}
if (ability.getPayCosts().hasTapCost() && !attacker.hasKeyword(Keyword.VIGILANCE)) {
continue;
}
if (!ComputerUtilCost.canPayCost(ability, attacker.getController())) {
continue;
}
if (ability.getApi() == ApiType.Pump) {
if (!ability.hasParam("NumDef")) {
continue;
}
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController())) {
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability);
if (tBonus > 0) {
toughness += tBonus;
}
}
toughness += AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("NumDef"), ability, true);
} else if (ability.getApi() == ApiType.PutCounter) {
if (!ability.hasParam("CounterType") || !ability.getParam("CounterType").equals("P1P1")) {
continue;
@@ -1693,15 +1623,13 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && attacker.getCounters(CounterEnumType.P1P1) > 0) {
continue;
}
if (!ability.getPayCosts().hasTapCost() && ComputerUtilCost.canPayCost(ability, attacker.getController())) {
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
if (tBonus > 0) {
toughness += tBonus;
}
int tBonus = AbilityUtils.calculateAmount(ability.getHostCard(), ability.getParam("CounterNum"), ability);
if (tBonus > 0) {
toughness += tBonus;
}
}
}
@@ -1848,10 +1776,10 @@ public class ComputerUtilCombat {
if (((attacker.hasKeyword(Keyword.INDESTRUCTIBLE) || (ComputerUtil.canRegenerate(ai, attacker) && !withoutAbilities))
&& !(blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT)))
|| (attacker.hasKeyword(Keyword.PERSIST) && !attacker.canReceiveCounters(CounterType.M1M1) && (attacker
.getCounters(CounterType.M1M1) == 0))
|| (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterType.P1P1) && (attacker
.getCounters(CounterType.P1P1) == 0))) {
|| (attacker.hasKeyword(Keyword.PERSIST) && !attacker.canReceiveCounters(CounterEnumType.M1M1) && (attacker
.getCounters(CounterEnumType.M1M1) == 0))
|| (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterEnumType.P1P1) && (attacker
.getCounters(CounterEnumType.P1P1) == 0))) {
return false;
}
@@ -2080,10 +2008,10 @@ public class ComputerUtilCombat {
if (((blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || (ComputerUtil.canRegenerate(ai, blocker) && !withoutAbilities)) && !(attacker
.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)))
|| (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterType.M1M1) && (blocker
.getCounters(CounterType.M1M1) == 0))
|| (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterType.P1P1) && (blocker
.getCounters(CounterType.P1P1) == 0))) {
|| (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterEnumType.M1M1) && (blocker
.getCounters(CounterEnumType.M1M1) == 0))
|| (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterEnumType.P1P1) && (blocker
.getCounters(CounterEnumType.P1P1) == 0))) {
return false;
}
@@ -2389,7 +2317,7 @@ public class ComputerUtilCombat {
restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
// Predict replacement effects
for (final Card ca : game.getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) {
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
for (final ReplacementEffect re : ca.getReplacementEffects()) {
Map<String, String> params = re.getMapParams();
if (!re.getMode().equals(ReplacementType.DamageDone) || !params.containsKey("PreventionEffect")) {
@@ -2517,7 +2445,7 @@ public class ComputerUtilCombat {
final Player controller = combatant.getController();
for (Card c : controller.getCardsIn(ZoneType.Battlefield)) {
for (SpellAbility ability : c.getAllSpellAbilities()) {
if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) {
if (!(ability instanceof AbilityActivated)) {
continue;
}
if (ability.getApi() != ApiType.Pump) {

View File

@@ -45,7 +45,7 @@ public class ComputerUtilCost {
final CostPutCounter addCounter = (CostPutCounter) part;
final CounterType type = addCounter.getCounter();
if (type.equals(CounterType.M1M1)) {
if (type.equals(CounterEnumType.M1M1)) {
return false;
}
}
@@ -75,7 +75,7 @@ public class ComputerUtilCost {
final CounterType type = remCounter.counter;
if (!part.payCostFromSource()) {
if (CounterType.P1P1.equals(type)) {
if (CounterEnumType.P1P1.equals(type)) {
return false;
}
continue;
@@ -97,7 +97,7 @@ public class ComputerUtilCost {
// check the sa what the PaymentDecision is.
// ignore Loyality abilities with Zero as Cost
if (sa != null && !CounterType.LOYALTY.equals(type)) {
if (sa != null && !CounterEnumType.LOYALTY.equals(type)) {
final AiCostDecision decision = new AiCostDecision(sa.getActivatingPlayer(), sa);
PaymentDecision pay = decision.visit(remCounter);
if (pay == null || pay.c <= 0) {
@@ -106,7 +106,7 @@ public class ComputerUtilCost {
}
//don't kill the creature
if (CounterType.P1P1.equals(type) && source.getLethalDamage() <= 1
if (CounterEnumType.P1P1.equals(type) && source.getLethalDamage() <= 1
&& !source.hasKeyword(Keyword.UNDYING)) {
return false;
}
@@ -467,9 +467,9 @@ public class ComputerUtilCost {
if(!meetsRestriction)
continue;
try {
if (StringUtils.isNumeric(parts[0])) {
extraManaNeeded += Integer.parseInt(parts[0]);
} catch (final NumberFormatException e) {
} else {
System.out.println("wrong SpellsNeedExtraMana SVar format on " + c);
}
}
@@ -480,9 +480,9 @@ public class ComputerUtilCost {
}
final String snem = c.getSVar("SpellsNeedExtraManaEffect");
if (!StringUtils.isBlank(snem)) {
try {
if (StringUtils.isNumeric(snem)) {
extraManaNeeded += Integer.parseInt(snem);
} catch (final NumberFormatException e) {
} else {
System.out.println("wrong SpellsNeedExtraManaEffect SVar format on " + c);
}
}
@@ -529,7 +529,7 @@ public class ComputerUtilCost {
public boolean apply(Card card) {
boolean hasManaSa = false;
for (final SpellAbility sa : card.getSpellAbilities()) {
if (sa.isManaAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) {
hasManaSa = true;
break;
}

View File

@@ -371,7 +371,7 @@ public class ComputerUtilMana {
adjustManaCostToAvoidNegEffects(cost, sa.getHostCard(), ai);
List<Mana> manaSpentToPay = test ? new ArrayList<>() : sa.getPayingMana();
boolean purePhyrexian = cost.containsOnlyPhyrexianMana();
int testEnergyPool = ai.getCounters(CounterType.ENERGY);
int testEnergyPool = ai.getCounters(CounterEnumType.ENERGY);
List<SpellAbility> paymentList = Lists.newArrayList();
@@ -507,16 +507,10 @@ public class ComputerUtilMana {
}
}
else {
if (saPayment.getPayCosts() != null) {
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
if (!pay.payComputerCosts(new AiCostDecision(ai, saPayment))) {
saList.remove(saPayment);
continue;
}
}
else {
System.err.println("Ability " + saPayment + " from " + saPayment.getHostCard() + " had NULL as payCost");
saPayment.getHostCard().tap();
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
if (!pay.payComputerCosts(new AiCostDecision(ai, saPayment))) {
saList.remove(saPayment);
continue;
}
ai.getGame().getStack().addAndUnfreeze(saPayment);
@@ -741,7 +735,7 @@ public class ComputerUtilMana {
continue;
}
if (thisMana.getManaAbility() != null && !thisMana.getManaAbility().meetsManaRestrictions(saBeingPaidFor)) {
if (thisMana.getManaAbility() != null && !thisMana.getManaAbility().meetsSpellAndShardRestrictions(saBeingPaidFor, shard, thisMana.getColor())) {
continue;
}
@@ -837,10 +831,9 @@ public class ComputerUtilMana {
if (checkCosts) {
// Check if AI can still play this mana ability
ma.setActivatingPlayer(ai);
if (ma.getPayCosts() != null) { // if the AI can't pay the additional costs skip the mana ability
if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) {
return false;
}
// if the AI can't pay the additional costs skip the mana ability
if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) {
return false;
}
else if (sourceCard.isTapped()) {
return false;
@@ -1144,7 +1137,7 @@ public class ComputerUtilMana {
ManaCostBeingPaid cost = new ManaCostBeingPaid(mana, restriction);
// Tack xMana Payments into mana here if X is a set value
if (sa.getPayCosts() != null && (cost.getXcounter() > 0 || extraMana > 0)) {
if (cost.getXcounter() > 0 || extraMana > 0) {
int manaToAdd = 0;
if (test && extraMana > 0) {
final int multiplicator = Math.max(cost.getXcounter(), 1);
@@ -1169,7 +1162,7 @@ public class ComputerUtilMana {
cost.increaseShard(shardToGrow, manaToAdd);
if (!test) {
card.setXManaCostPaid(manaToAdd / cost.getXcounter());
sa.setXManaCostPaid(manaToAdd / cost.getXcounter());
}
}
@@ -1218,7 +1211,7 @@ public class ComputerUtilMana {
for (SpellAbility ma : src.getManaAbilities()) {
ma.setActivatingPlayer(p);
if (!checkPlayable || ma.canPlay()) {
int costsToActivate = ma.getPayCosts() != null && ma.getPayCosts().getCostMana() != null ? ma.getPayCosts().getCostMana().convertAmount() : 0;
int costsToActivate = ma.getPayCosts().getCostMana() != null ? ma.getPayCosts().getCostMana().convertAmount() : 0;
int producedMana = ma.getParamOrDefault("Produced", "").split(" ").length;
int producedAmount = AbilityUtils.calculateAmount(src, ma.getParamOrDefault("Amount", "1"), ma);
@@ -1594,7 +1587,7 @@ public class ComputerUtilMana {
}
public static int determineMaxAffordableX(Player ai, SpellAbility sa) {
if (sa.getPayCosts() == null || sa.getPayCosts().getCostMana() == null) {
if (sa.getPayCosts().getCostMana() == null) {
return -1;
}

View File

@@ -5,7 +5,7 @@ import com.google.common.base.Function;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.cost.CostPayEnergy;
import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordInterface;
@@ -242,11 +242,11 @@ public class CreatureEvaluator implements Function<Card, Integer> {
&& "+X".equals(sa.getParam("NumDef"))
&& !sa.usesTargeting()
&& (!sa.hasParam("Defined") || "Self".equals(sa.getParam("Defined")))) {
if (sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
if (sa.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
// Electrostatic Pummeler, can be expanded for similar cards
int initPower = getEffectivePower(sa.getHostCard());
int pumpedPower = initPower;
int energy = sa.getHostCard().getController().getCounters(CounterType.ENERGY);
int energy = sa.getHostCard().getController().getCounters(CounterEnumType.ENERGY);
if (energy > 0) {
int numActivations = energy / 3;
for (int i = 0; i < numActivations; i++) {

View File

@@ -9,12 +9,10 @@ import forge.card.CardStateName;
import forge.card.MagicColor;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.ability.AbilityFactory;
import forge.game.ability.effects.DetachedCardEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CounterType;
import forge.game.card.*;
import forge.game.card.token.TokenInfo;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
@@ -24,6 +22,7 @@ import forge.game.mana.ManaPool;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.ability.AbilityKey;
import forge.game.trigger.TriggerType;
@@ -244,7 +243,7 @@ public abstract class GameState {
if (card instanceof DetachedCardEffect) {
continue;
}
addCard(zone, card.getOwner() == ai ? aiCardTexts : humanCardTexts, card);
addCard(zone, card.getController() == ai ? aiCardTexts : humanCardTexts, card);
}
}
}
@@ -271,6 +270,10 @@ public abstract class GameState {
}
if (zoneType == ZoneType.Battlefield) {
if (c.getOwner() != c.getController()) {
// TODO: Handle more than 2-player games.
newText.append("|Owner:" + (c.getOwner().isAI() ? "AI" : "Human"));
}
if (c.isTapped()) {
newText.append("|Tapped");
}
@@ -362,6 +365,12 @@ public abstract class GameState {
if (c.isFaceDown()) {
newText.append("|FaceDown"); // Exiled face down
}
if (c.isAdventureCard() && c.getZone().is(ZoneType.Exile)) {
// TODO: this will basically default all exiled cards with Adventure to being "On Adventure".
// Need to figure out a better way to detect if it's actually on adventure.
newText.append("|OnAdventure");
}
}
if (zoneType == ZoneType.Battlefield || zoneType == ZoneType.Exile) {
@@ -583,6 +592,7 @@ public abstract class GameState {
cardToEnchantPlayerId.clear();
cardToRememberedId.clear();
cardToExiledWithId.clear();
cardToImprintedId.clear();
markedDamage.clear();
cardToChosenClrs.clear();
cardToChosenCards.clear();
@@ -807,6 +817,12 @@ public abstract class GameState {
break;
}
}
if (sa.hasParam("RememberTargets")) {
for (final GameObject o : sa.getTargets().getTargets()) {
sa.getHostCard().addRemembered(o);
}
}
}
private void handleScriptExecution(final Game game) {
@@ -966,14 +982,27 @@ public abstract class GameState {
spellDef = spellDef.substring(0, spellDef.indexOf("->")).trim();
}
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
Card c = null;
if (pc == null) {
System.err.println("ERROR: Could not find a card with name " + spellDef + " to precast!");
return;
if (StringUtils.isNumeric(spellDef)) {
// Precast from a specific host
c = idToCard.get(Integer.parseInt(spellDef));
if (c == null) {
System.err.println("ERROR: Could not find a card with ID " + spellDef + " to precast!");
return;
}
} else {
// Precast from a card by name
PaperCard pc = StaticData.instance().getCommonCards().getCard(spellDef);
if (pc == null) {
System.err.println("ERROR: Could not find a card with name " + spellDef + " to precast!");
return;
}
c = Card.fromPaperCard(pc, activator);
}
Card c = Card.fromPaperCard(pc, activator);
SpellAbility sa = null;
if (!scriptID.isEmpty()) {
@@ -1062,11 +1091,11 @@ public abstract class GameState {
}
private void applyCountersToGameEntity(GameEntity entity, String counterString) {
entity.setCounters(Maps.newEnumMap(CounterType.class));
entity.setCounters(Maps.newHashMap());
String[] allCounterStrings = counterString.split(",");
for (final String counterPair : allCounterStrings) {
String[] pair = counterPair.split("=", 2);
entity.addCounter(CounterType.valueOf(pair[0]), Integer.parseInt(pair[1]), null, false, false, null);
entity.addCounter(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, false, false, null);
}
}
@@ -1108,7 +1137,7 @@ public abstract class GameState {
Map<CounterType, Integer> counters = c.getCounters();
// Note: Not clearCounters() since we want to keep the counters
// var as-is.
c.setCounters(Maps.newEnumMap(CounterType.class));
c.setCounters(Maps.newHashMap());
if (c.isAura()) {
// dummy "enchanting" to indicate that the card will be force-attached elsewhere
// (will be overridden later, so the actual value shouldn't matter)
@@ -1202,6 +1231,16 @@ public abstract class GameState {
c.setState(CardStateName.Flipped, true);
} else if (info.startsWith("Meld")) {
c.setState(CardStateName.Meld, true);
} else if (info.startsWith("OnAdventure")) {
String abAdventure = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
AbilitySub saAdventure = (AbilitySub)AbilityFactory.getAbility(abAdventure, c);
StringBuilder sbPlay = new StringBuilder();
sbPlay.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure");
sbPlay.append(" | AffectedZone$ Exile | Description$ You may cast the card.");
saAdventure.setSVar("Play", sbPlay.toString());
saAdventure.setActivatingPlayer(c.getOwner());
saAdventure.resolve();
c.setExiledWith(c); // This seems to be the way it's set up internally. Potentially not needed here?
} else if (info.startsWith("IsCommander")) {
// TODO: This doesn't seem to properly restore the ability to play the commander. Why?
c.setCommander(true);

View File

@@ -145,12 +145,12 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional) {
return brains.chooseCardsForEffect(sourceList, sa, min, max, isOptional);
public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional, Map<String, Object> params) {
return brains.chooseCardsForEffect(sourceList, sa, min, max, isOptional, params);
}
@Override
public <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) {
public <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
if (delayedReveal != null) {
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
@@ -158,13 +158,13 @@ public class PlayerControllerAi extends PlayerController {
if (null == api) {
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
}
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer);
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer, params);
}
@Override
public <T extends GameEntity> List<T> chooseEntitiesForEffect(
FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title,
Player targetedPlayer) {
Player targetedPlayer, Map<String, Object> params) {
if (delayedReveal != null) {
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
@@ -172,7 +172,7 @@ public class PlayerControllerAi extends PlayerController {
List<T> selecteds = new ArrayList<>();
T selected;
do {
selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer);
selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer, params);
if ( selected != null ) {
remaining.remove(selected);
selecteds.add(selected);
@@ -182,7 +182,23 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title,
public List<SpellAbility> chooseSpellAbilitiesForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
int num, Map<String, Object> params) {
List<SpellAbility> remaining = Lists.newArrayList(spells);
List<SpellAbility> selecteds = Lists.newArrayList();
SpellAbility selected;
do {
selected = chooseSingleSpellForEffect(remaining, sa, title, params);
if ( selected != null ) {
remaining.remove(selected);
selecteds.add(selected);
}
} while ( (selected != null ) && (selecteds.size() < num) );
return selecteds;
}
@Override
public SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
Map<String, Object> params) {
ApiType api = sa.getApi();
if (null == api) {
@@ -208,15 +224,13 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public boolean confirmTrigger(WrappedAbility wrapper, Map<String, String> triggerParams, boolean isMandatory) {
public boolean confirmTrigger(WrappedAbility wrapper) {
final SpellAbility sa = wrapper.getWrappedAbility();
//final Trigger regtrig = wrapper.getTrigger();
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Deathmist Raptor")) {
return true;
}
if (triggerParams.containsKey("DelayedTrigger") || isMandatory) {
//TODO: The only card with an optional delayed trigger is Shirei, Shizo's Caretaker,
// needs to be expanded when a more difficult cards comes up
if (wrapper.isMandatory()) {
return true;
}
// Store/replace target choices more properly to get this SA cleared.
@@ -502,20 +516,19 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public String chooseSomeType(String kindOfType, SpellAbility sa, List<String> validTypes, List<String> invalidTypes, boolean isOptional) {
String chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), invalidTypes);
if (StringUtils.isBlank(chosen) && !validTypes.isEmpty())
{
chosen = validTypes.get(0);
System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to 1st element: chosen");
public String chooseSomeType(String kindOfType, SpellAbility sa, Collection<String> validTypes, List<String> invalidTypes, boolean isOptional) {
String chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), validTypes, invalidTypes);
if (StringUtils.isBlank(chosen) && !validTypes.isEmpty()) {
chosen = validTypes.iterator().next();
System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to arbitrary element: chosen");
}
game.getAction().nofityOfValue(sa, player, chosen, player);
return chosen;
}
@Override
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes) {
return ComputerUtil.vote(player, options, sa, votes);
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer) {
return ComputerUtil.vote(player, options, sa, votes, forPlayer);
}
@Override
@@ -604,6 +617,7 @@ public class PlayerControllerAi extends PlayerController {
if (sa instanceof LandAbility) {
if (sa.canPlay()) {
sa.resolve();
game.updateLastStateForCard(sa.getHostCard());
}
} else {
ComputerUtil.handlePlayingSpellAbility(player, sa, game);
@@ -755,6 +769,7 @@ public class PlayerControllerAi extends PlayerController {
return defaultVal != null && defaultVal.booleanValue();
case UntapTimeVault: return false; // TODO Should AI skip his turn for time vault?
case LeftOrRight: return brains.chooseDirection(sa);
case OddsOrEvens: return brains.chooseEvenOdd(sa); // false is Odd, true is Even
default:
return MyRandom.getRandom().nextBoolean();
}
@@ -1123,7 +1138,8 @@ public class PlayerControllerAi extends PlayerController {
CardCollectionView cards = CardLists.getValidCards(aiLibrary, "Creature", player, sa.getHostCard());
return ComputerUtilCard.getMostProminentCardName(cards);
} else if (logic.equals("BestCreatureInComputerDeck")) {
return ComputerUtilCard.getBestCreatureAI(aiLibrary).getName();
Card bestCreature = ComputerUtilCard.getBestCreatureAI(aiLibrary);
return bestCreature != null ? bestCreature.getName() : "Plains";
} else if (logic.equals("RandomInComputerDeck")) {
return Aggregates.random(aiLibrary).getName();
} else if (logic.equals("MostProminentSpellInComputerDeck")) {
@@ -1214,7 +1230,7 @@ public class PlayerControllerAi extends PlayerController {
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen,
List<OptionalCostValue> optionalCostValues) {
List<OptionalCostValue> chosenOptCosts = Lists.newArrayList();
Cost costSoFar = chosen.getPayCosts() != null ? chosen.getPayCosts().copy() : Cost.Zero;
Cost costSoFar = chosen.getPayCosts().copy();
for (OptionalCostValue opt : optionalCostValues) {
// Choose the optional cost if it can be paid (to be improved later, check for playability and other conditions perhaps)
@@ -1253,7 +1269,7 @@ public class PlayerControllerAi extends PlayerController {
// TODO: improve the logic depending on the keyword and the playability of the cost-modified SA (enough targets present etc.)
int chosenAmount = 0;
Cost costSoFar = sa.getPayCosts() != null ? sa.getPayCosts().copy() : Cost.Zero;
Cost costSoFar = sa.getPayCosts().copy();
for (int i = 0; i < max; i++) {
costSoFar.add(cost);
@@ -1269,13 +1285,16 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, SpellAbility sa, String title) {
public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, SpellAbility sa, String title, boolean isOptional) {
CardCollection choices = new CardCollection();
for (String mapKey: validMap.keySet()) {
CardCollection cc = validMap.get(mapKey);
cc.removeAll(choices);
choices.add(ComputerUtilCard.getBestAI(cc)); // TODO: should the AI limit itself here with the max number of cards in hand?
Card chosen = ComputerUtilCard.getBestAI(cc);
if (chosen != null) {
choices.add(chosen);
}
}
return choices;

View File

@@ -327,7 +327,7 @@ public class SpecialCardAi {
boolean canTrample = source.hasKeyword(Keyword.TRAMPLE);
if (!isBlocking && combat.getDefenderByAttacker(source) instanceof Card) {
int loyalty = combat.getDefenderByAttacker(source).getCounters(CounterType.LOYALTY);
int loyalty = combat.getDefenderByAttacker(source).getCounters(CounterEnumType.LOYALTY);
int totalDamageToPW = 0;
for (Card atk : (combat.getAttackersOf(combat.getDefenderByAttacker(source)))) {
if (combat.isUnblocked(atk)) {
@@ -407,7 +407,7 @@ public class SpecialCardAi {
}
public static Pair<Integer, Integer> getPumpedPT(Player ai, int power, int toughness) {
int energy = ai.getCounters(CounterType.ENERGY);
int energy = ai.getCounters(CounterEnumType.ENERGY);
if (energy > 0) {
int numActivations = energy / 3;
for (int i = 0; i < numActivations; i++) {
@@ -708,7 +708,7 @@ public class SpecialCardAi {
// if there's another reanimator card currently suspended, don't cast a new one until the previous
// one resolves, otherwise the reanimation attempt will be ruined (e.g. Living End)
for (Card ex : ai.getCardsIn(ZoneType.Exile)) {
if (ex.hasSVar("IsReanimatorCard") && ex.getCounters(CounterType.TIME) > 0) {
if (ex.hasSVar("IsReanimatorCard") && ex.getCounters(CounterEnumType.TIME) > 0) {
return false;
}
}
@@ -767,7 +767,7 @@ public class SpecialCardAi {
Player controller = c.getController();
boolean wasCaged = false;
for (Card caged : CardLists.filter(controller.getCardsIn(ZoneType.Exile),
CardPredicates.hasCounter(CounterType.CAGE))) {
CardPredicates.hasCounter(CounterEnumType.CAGE))) {
if (c.getName().equals(caged.getName())) {
wasCaged = true;
break;
@@ -1073,7 +1073,7 @@ public class SpecialCardAi {
// Sarkhan the Mad
public static class SarkhanTheMad {
public static boolean considerDig(final Player ai, final SpellAbility sa) {
return sa.getHostCard().getCounters(CounterType.LOYALTY) == 1;
return sa.getHostCard().getCounters(CounterEnumType.LOYALTY) == 1;
}
public static boolean considerMakeDragon(final Player ai, final SpellAbility sa) {
@@ -1109,7 +1109,7 @@ public class SpecialCardAi {
// Sorin, Vengeful Bloodlord
public static class SorinVengefulBloodlord {
public static boolean consider(final Player ai, final SpellAbility sa) {
int loyalty = sa.getHostCard().getCounters(CounterType.LOYALTY);
int loyalty = sa.getHostCard().getCounters(CounterEnumType.LOYALTY);
CardCollection creaturesToGet = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard),
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.lessCMC(loyalty - 1), new Predicate<Card>() {
@Override
@@ -1295,6 +1295,26 @@ public class SpecialCardAi {
}
}
// Timmerian Fiends
public static class TimmerianFiends {
public static boolean consider(final Player ai, final SpellAbility sa) {
final Card targeted = sa.getParentTargetingCard().getTargetCard();
if (targeted == null) {
return false;
}
if (targeted.isCreature()) {
if (ComputerUtil.aiLifeInDanger(ai, true, 0)) {
return true; // do it, hoping to save a valuable potential blocker etc.
}
return ComputerUtilCard.evaluateCreature(targeted) >= 200; // might need tweaking
} else {
// TODO: this currently compares purely by CMC. To be somehow improved, especially for stuff like the Power Nine etc.
return ComputerUtilCard.evaluatePermanentList(new CardCollection(targeted)) >= 3;
}
}
}
// Volrath's Shapeshifter
public static class VolrathsShapeshifter {
public static boolean consider(final Player ai, final SpellAbility sa) {
@@ -1345,7 +1365,7 @@ public class SpecialCardAi {
Card source = sa.getHostCard();
Game game = source.getGame();
final int loyalty = source.getCounters(CounterType.LOYALTY);
final int loyalty = source.getCounters(CounterEnumType.LOYALTY);
int x = -1, best = 0;
Card single = null;
for (int i = 0; i < loyalty; i++) {

View File

@@ -2,6 +2,7 @@ package forge.ai;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.card.CardStateName;
import forge.card.ICardFace;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser;
@@ -167,7 +168,8 @@ public abstract class SpellAbilityAi {
// a mandatory SpellAbility with targeting but without candidates,
// does not need to go any deeper
if (sa.usesTargeting() && mandatory && !sa.getTargetRestrictions().hasCandidates(sa, true)) {
if (sa.usesTargeting() && mandatory && !sa.isTargetNumberValid()
&& !sa.getTargetRestrictions().hasCandidates(sa, true)) {
return false;
}
@@ -247,6 +249,7 @@ public abstract class SpellAbilityAi {
protected static boolean isSorcerySpeed(final SpellAbility sa) {
return (sa.getRootAbility().isSpell() && sa.getHostCard().isSorcery())
|| (sa.getRootAbility().isAbility() && sa.getRestrictions().isSorcerySpeed())
|| (sa.getRootAbility().isAdventure() && sa.getHostCard().getState(CardStateName.Adventure).getType().isSorcery())
|| (sa.isPwAbility() && !sa.getHostCard().hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed."));
}
@@ -264,7 +267,7 @@ public abstract class SpellAbilityAi {
// TODO probably also consider if winter orb or similar are out
if (sa.getPayCosts() == null || sa instanceof AbilitySub) {
if (sa instanceof AbilitySub) {
return true; // This is only true for Drawbacks and triggers
}
@@ -304,7 +307,7 @@ public abstract class SpellAbilityAi {
}
@SuppressWarnings("unchecked")
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
boolean hasPlayer = false;
boolean hasCard = false;
boolean hasPlaneswalker = false;
@@ -321,11 +324,11 @@ public abstract class SpellAbilityAi {
}
if (hasPlayer && hasPlaneswalker) {
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options);
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options, params);
} else if (hasCard) {
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer);
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer, params);
} else if (hasPlayer) {
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options);
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options, params);
}
return null;
@@ -336,17 +339,17 @@ public abstract class SpellAbilityAi {
return spells.get(0);
}
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayer is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayerOrPlaneswalker is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}

View File

@@ -33,7 +33,7 @@ public enum SpellApiToAi {
.put(ApiType.BidLife, BidLifeAi.class)
.put(ApiType.Bond, BondAi.class)
.put(ApiType.Branch, AlwaysPlayAi.class)
.put(ApiType.ChangeCombatants, CannotPlayAi.class)
.put(ApiType.ChangeCombatants, ChangeCombatantsAi.class)
.put(ApiType.ChangeTargets, ChangeTargetsAi.class)
.put(ApiType.ChangeX, AlwaysPlayAi.class)
.put(ApiType.ChangeZone, ChangeZoneAi.class)
@@ -42,6 +42,7 @@ public enum SpellApiToAi {
.put(ApiType.ChooseCard, ChooseCardAi.class)
.put(ApiType.ChooseColor, ChooseColorAi.class)
.put(ApiType.ChooseDirection, ChooseDirectionAi.class)
.put(ApiType.ChooseEvenOdd, ChooseEvenOddAi.class)
.put(ApiType.ChooseNumber, ChooseNumberAi.class)
.put(ApiType.ChoosePlayer, ChoosePlayerAi.class)
.put(ApiType.ChooseSource, ChooseSourceAi.class)
@@ -83,7 +84,7 @@ public enum SpellApiToAi {
.put(ApiType.FlipACoin, FlipACoinAi.class)
.put(ApiType.Fog, FogAi.class)
.put(ApiType.GainControl, ControlGainAi.class)
.put(ApiType.GainControlVariant, AlwaysPlayAi.class)
.put(ApiType.GainControlVariant, ControlGainVariantAi.class)
.put(ApiType.GainLife, LifeGainAi.class)
.put(ApiType.GainOwnership, CannotPlayAi.class)
.put(ApiType.GameDrawn, CannotPlayAi.class)
@@ -91,6 +92,7 @@ public enum SpellApiToAi {
.put(ApiType.Goad, GoadAi.class)
.put(ApiType.Haunt, HauntAi.class)
.put(ApiType.ImmediateTrigger, AlwaysPlayAi.class)
.put(ApiType.Investigate, InvestigateAi.class)
.put(ApiType.LoseLife, LifeLoseAi.class)
.put(ApiType.LosesGame, GameLossAi.class)
.put(ApiType.Mana, ManaEffectAi.class)

View File

@@ -1,5 +1,7 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -23,12 +25,12 @@ public class AmassAi extends SpellAbilityAi {
final Game game = ai.getGame();
if (!aiArmies.isEmpty()) {
return CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterType.P1P1)) > 0;
return CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterEnumType.P1P1)) > 0;
} else {
final String tokenScript = "b_0_0_zombie_army";
final int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Num", "1"), sa);
Card token = TokenInfo.getProtoType(tokenScript, sa);
Card token = TokenInfo.getProtoType(tokenScript, sa, false);
if (token == null) {
return false;
@@ -44,8 +46,8 @@ public class AmassAi extends SpellAbilityAi {
CardCollection preList = new CardCollection(token);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(token), preList);
if (token.canReceiveCounters(CounterType.P1P1)) {
token.setCounters(CounterType.P1P1, amount);
if (token.canReceiveCounters(CounterEnumType.P1P1)) {
token.setCounters(CounterEnumType.P1P1, amount);
}
if (token.isCreature() && token.getNetToughness() < 1) {
@@ -86,8 +88,8 @@ public class AmassAi extends SpellAbilityAi {
}
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
Iterable<Card> better = CardLists.filter(options, CardPredicates.canReceiveCounters(CounterType.P1P1));
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
Iterable<Card> better = CardLists.filter(options, CardPredicates.canReceiveCounters(CounterEnumType.P1P1));
if (Iterables.isEmpty(better)) {
better = options;
}

View File

@@ -247,7 +247,6 @@ public class AnimateAi extends SpellAbilityAi {
final Player ai = sa.getActivatingPlayer();
final PhaseHandler ph = ai.getGame().getPhaseHandler();
final boolean alwaysActivatePWAbility = sa.hasParam("Planeswalker")
&& sa.getPayCosts() != null
&& sa.getPayCosts().hasSpecificCostType(CostPutCounter.class)
&& sa.getTargetRestrictions() != null
&& sa.getTargetRestrictions().getMinTargets(sa.getHostCard(), sa) == 0;

View File

@@ -398,7 +398,7 @@ public class AttachAi extends SpellAbilityAi {
if (!c.isCreature() && !c.getType().hasSubtype("Vehicle") && !c.isTapped()) {
// try to identify if this thing can actually tap
for (SpellAbility ab : c.getAllSpellAbilities()) {
if (ab.getPayCosts() != null && ab.getPayCosts().hasTapCost()) {
if (ab.getPayCosts().hasTapCost()) {
return true;
}
}
@@ -560,7 +560,7 @@ public class AttachAi extends SpellAbilityAi {
@Override
public boolean apply(final Card c) {
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.isAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
if (sa.isAbility() && sa.getPayCosts().hasTapCost()) {
return false;
}
}
@@ -1031,7 +1031,7 @@ public class AttachAi extends SpellAbilityAi {
Card c = null;
List<Card> magnetList = null;
String stCheck = null;
if (attachSource.isAura() || sa.hasParam("Bestow")) {
if (attachSource.isAura() || sa.isBestow()) {
stCheck = "EnchantedBy";
magnetList = CardLists.filter(list, new Predicate<Card>() {
@Override
@@ -1704,12 +1704,12 @@ public class AttachAi extends SpellAbilityAi {
}
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return attachToCardAIPreferences(ai, sa, true);
}
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
return attachToPlayerAIPreferences(ai, sa, true);
}
}

View File

@@ -17,6 +17,8 @@
*/
package forge.ai.ability;
import java.util.Map;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
@@ -50,7 +52,7 @@ public final class BondAi extends SpellAbilityAi {
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return ComputerUtilCard.getBestCreatureAI(options);
}

View File

@@ -0,0 +1,66 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.GameEntity;
import forge.game.player.Player;
import forge.game.player.PlayerCollection;
import forge.game.player.PlayerPredicates;
import forge.game.spellability.SpellAbility;
import java.util.Collection;
import java.util.Map;
public class ChangeCombatantsAi extends SpellAbilityAi {
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
*/
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
// TODO: Extend this if possible for cards that have this as an activated ability
return false;
}
@Override
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
return mandatory || canPlayAI(aiPlayer, sa);
}
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#chkAIDrawback(java.util.Map, forge.card.spellability.SpellAbility, forge.game.player.Player)
*/
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
final String logic = sa.getParamOrDefault("AILogic", "");
if (logic.equals("WeakestOppExceptCtrl")) {
PlayerCollection targetableOpps = aiPlayer.getOpponents();
targetableOpps.remove(sa.getHostCard().getController());
if (targetableOpps.isEmpty()) {
return false;
}
return true;
}
return false;
}
@Override
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
PlayerCollection targetableOpps = new PlayerCollection();
for (GameEntity p : options) {
if (p instanceof Player && !p.equals(sa.getHostCard().getController())) {
Player pp = (Player)p;
if (pp.isOpponentOf(ai)) {
targetableOpps.add(pp);
}
}
}
Player weakestTargetableOpp = targetableOpps.filter(PlayerPredicates.isTargetableBy(sa))
.min(PlayerPredicates.compareByLife());
return (T)weakestTargetableOpp;
}
}

View File

@@ -142,7 +142,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* </p>
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
*
*
* @return a boolean.
*/
@Override
@@ -170,7 +170,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
*
*
* @return a boolean.
*/
@Override
@@ -370,10 +370,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (!activateForCost && list.isEmpty()) {
return false;
}
if ("Atarka's Command".equals(sourceName)
&& (list.size() < 2 || ai.getLandsPlayedThisTurn() < 1)) {
// be strict on playing lands off charms
return false;
if ("Atarka's Command".equals(sourceName)
&& (list.size() < 2 || ai.getLandsPlayedThisTurn() < 1)) {
// be strict on playing lands off charms
return false;
}
String num = sa.getParam("ChangeNum");
@@ -385,7 +385,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
source.setSVar("PayX", Integer.toString(xPay));
}
}
if (sourceName.equals("Temur Sabertooth")) {
// activated bounce + pump
if (ComputerUtilCard.shouldPumpCard(ai, sa.getSubAbility(), source, 0, 0, Arrays.asList("Indestructible")) ||
@@ -400,9 +400,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
}
if (ComputerUtil.playImmediately(ai, sa)) {
return true;
return true;
}
// don't use fetching to top of library/graveyard before main2
@@ -418,9 +418,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
if (ComputerUtil.waitForBlocking(sa)) {
return false;
return false;
}
final AbilitySub subAb = sa.getSubAbility();
return subAb == null || SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb);
}
@@ -551,7 +551,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* basicManaFixing.
* </p>
* @param ai
*
*
* @param list
* a List<Card> object.
* @return a {@link forge.game.card.Card} object.
@@ -584,7 +584,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (minType != null) {
result = CardLists.getType(list, minType);
}
// pick dual lands if available
if (Iterables.any(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS))) {
result = CardLists.filter(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
@@ -597,7 +597,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* <p>
* areAllBasics.
* </p>
*
*
* @param types
* a {@link java.lang.String} object.
* @return a boolean.
@@ -617,8 +617,8 @@ public class ChangeZoneAi extends SpellAbilityAi {
* @return Card
*/
private static Card chooseCreature(final Player ai, CardCollection list) {
// Creating a new combat for testing purposes.
final Player opponent = ai.getWeakestOpponent();
// Creating a new combat for testing purposes.
final Player opponent = ai.getWeakestOpponent();
Combat combat = new Combat(opponent);
for (Card att : opponent.getCreaturesInPlay()) {
combat.addAttacker(att, ai);
@@ -742,7 +742,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
@@ -781,7 +781,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
return false;
}
}
//don't unearth after attacking is possible
if (sa.hasParam("Unearth") && ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
return false;
@@ -895,7 +895,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
return false;
}
immediately |= ComputerUtil.playImmediately(ai, sa);
// Narrow down the list:
@@ -926,7 +926,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
CardCollection blockers = currCombat.getBlockers(attacker);
// Save my attacker by bouncing a blocker
if (attacker.getController().equals(ai) && attacker.getShieldCount() == 0
&& ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)
&& ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)
&& !currCombat.getBlockers(attacker).isEmpty()) {
ComputerUtilCard.sortByEvaluateCreature(blockers);
Combat combat = new Combat(ai);
@@ -970,9 +970,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
sa.getTargets().add(tobounce);
boolean saheeliFelidarCombo = sa.getHostCard().getName().equals("Felidar Guardian")
boolean saheeliFelidarCombo = sa.getHostCard().getName().equals("Felidar Guardian")
&& tobounce.getName().equals("Saheeli Rai")
&& CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Felidar Guardian")).size() <
&& CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Felidar Guardian")).size() <
CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.isType("Creature")).size() + ai.getOpponentsGreatestLifeTotal() + 10;
// remember that the card was bounced already unless it's a special combo case
@@ -985,20 +985,20 @@ public class ChangeZoneAi extends SpellAbilityAi {
// bounce opponent's stuff
list = CardLists.filterControlledBy(list, ai.getOpponents());
if (!CardLists.getNotType(list, "Land").isEmpty()) {
// When bouncing opponents stuff other than lands, don't bounce cards with CMC 0
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
for (Card aura : c.getEnchantedBy()) {
// When bouncing opponents stuff other than lands, don't bounce cards with CMC 0
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
for (Card aura : c.getEnchantedBy()) {
return aura.getController().isOpponentOf(ai);
}
if (blink) {
return c.isToken();
} else {
return c.isToken() || c.getCMC() > 0;
}
}
});
}
if (blink) {
return c.isToken();
} else {
return c.isToken() || c.getCMC() > 0;
}
}
});
}
// TODO: Blink permanents with ETB triggers
/*else if (!sa.isTrigger() && SpellAbilityAi.playReusable(ai, sa)) {
@@ -1023,7 +1023,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
} else if (origin.contains(ZoneType.Graveyard)) {
if (destination.equals(ZoneType.Exile) || destination.equals(ZoneType.Library)) {
if (destination.equals(ZoneType.Exile) || destination.equals(ZoneType.Library)) {
// Don't use these abilities before main 2 if possible
if (!immediately && game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
&& !sa.hasParam("ActivationPhases") && !ComputerUtil.castSpellInMain1(ai, sa)) {
@@ -1035,7 +1035,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
&& !ComputerUtil.activateForCost(sa, ai)) {
return false;
}
} else if (destination.equals(ZoneType.Hand)) {
} else if (destination.equals(ZoneType.Hand)) {
// only retrieve cards from computer graveyard
list = CardLists.filterControlledBy(list, ai);
} else if (sa.hasParam("AttachedTo")) {
@@ -1065,8 +1065,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (destination.equals(ZoneType.Exile) || origin.contains(ZoneType.Battlefield)) {
// don't rush bouncing stuff when not going to attack
if (!immediately && sa.getPayCosts() != null
&& game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
if (!immediately && game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
&& game.getPhaseHandler().isPlayerTurn(ai)
&& ai.getCreaturesInPlay().isEmpty()) {
return false;
@@ -1097,14 +1096,14 @@ public class ChangeZoneAi extends SpellAbilityAi {
// Only care about combatants during combat
if (game.getPhaseHandler().inCombat() && origin.contains(ZoneType.Battlefield)) {
CardCollection newList = CardLists.getValidCards(list, "Card.attacking,Card.blocking", null, null);
if (!newList.isEmpty() || !sa.isTrigger()) {
list = newList;
}
CardCollection newList = CardLists.getValidCards(list, "Card.attacking,Card.blocking", null, null);
if (!newList.isEmpty() || !sa.isTrigger()) {
list = newList;
}
}
boolean doWithoutTarget = sa.hasParam("Planeswalker") && sa.getTargetRestrictions() != null
&& sa.getTargetRestrictions().getMinTargets(source, sa) == 0 && sa.getPayCosts() != null
boolean doWithoutTarget = sa.hasParam("Planeswalker") && sa.usesTargeting()
&& sa.getTargetRestrictions().getMinTargets(source, sa) == 0
&& sa.getPayCosts().hasSpecificCostType(CostPutCounter.class);
if (list.isEmpty() && !doWithoutTarget) {
@@ -1252,7 +1251,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
return true;
}
/**
* Checks if a permanent threatened by a stack ability or in combat can
* be saved by bouncing.
@@ -1325,11 +1324,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
Collections.sort(aiPlaneswalkers, new Comparator<Card>() {
@Override
public int compare(final Card a, final Card b) {
return a.getCounters(CounterType.LOYALTY) - b.getCounters(CounterType.LOYALTY);
return a.getCounters(CounterEnumType.LOYALTY) - b.getCounters(CounterEnumType.LOYALTY);
}
});
for (Card pw : aiPlaneswalkers) {
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
int freshLoyalty = Integer.valueOf(pw.getCurrentState().getBaseLoyalty());
if (freshLoyalty - curLoyalty >= loyaltyDiff && curLoyalty <= maxLoyaltyToConsider) {
return pw;
@@ -1507,10 +1506,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (type == null) {
type = "Card";
}
Card c = null;
final Player activator = sa.getActivatingPlayer();
CardLists.shuffle(fetchList);
// Save a card as a default, in case we can't find anything suitable.
Card first = fetchList.get(0);
@@ -1615,19 +1614,19 @@ public class ChangeZoneAi extends SpellAbilityAi {
// AI was never asked
return true;
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Called when looking for creature to attach aura or equipment
return ComputerUtilCard.getBestAI(options);
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List)
*/
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
// Currently only used by Curse of Misfortunes, so this branch should never get hit
// But just in case it does, just select the first option
return Iterables.getFirst(options, null);
@@ -1790,6 +1789,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
public boolean doReturnCommanderLogic(SpellAbility sa, Player aiPlayer) {
@SuppressWarnings("unchecked")
Map<AbilityKey, Object> originalParams = (Map<AbilityKey, Object>)sa.getReplacingObject(AbilityKey.OriginalParams);
SpellAbility causeSa = (SpellAbility)originalParams.get(AbilityKey.Cause);
SpellAbility causeSub = null;
@@ -1801,7 +1801,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (causeSa != null && (causeSub = causeSa.getSubAbility()) != null) {
ApiType subApi = causeSub.getApi();
if (subApi == ApiType.ChangeZone && "Exile".equals(causeSub.getParam("Origin"))
&& "Battlefield".equals(causeSub.getParam("Destination"))) {
// A blink effect implemented using ChangeZone API
@@ -1817,7 +1817,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} else return causeSa.getHostCard() == null || !causeSa.getHostCard().equals(sa.getReplacingObject(AbilityKey.Card))
|| !causeSa.getActivatingPlayer().equals(aiPlayer);
}
// Normally we want the commander back in Command zone to recast him later
return true;
}

View File

@@ -11,6 +11,7 @@ import forge.util.MyRandom;
import forge.util.collect.FCollection;
import java.util.List;
import java.util.Map;
public class CharmAi extends SpellAbilityAi {
@Override
@@ -232,7 +233,7 @@ public class CharmAi extends SpellAbilityAi {
}
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> opponents) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> opponents, Map<String, Object> params) {
return Aggregates.random(opponents);
}
}

View File

@@ -2,6 +2,7 @@ package forge.ai.ability;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
@@ -20,7 +21,7 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.combat.Combat;
import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType;
@@ -99,7 +100,7 @@ public class ChooseCardAi extends SpellAbilityAi {
});
return !choices.isEmpty();
} else if (aiLogic.equals("Ashiok")) {
final int loyalty = host.getCounters(CounterType.LOYALTY) - 1;
final int loyalty = host.getCounters(CounterEnumType.LOYALTY) - 1;
for (int i = loyalty; i >= 0; i--) {
host.setSVar("ChosenX", "Number$" + i);
choices = ai.getGame().getCardsIn(choiceZone);
@@ -140,12 +141,12 @@ public class ChooseCardAi extends SpellAbilityAi {
}
return checkApiLogic(ai, sa);
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
final Card host = sa.getHostCard();
final Player ctrl = host.getController();
final String logic = sa.getParam("AILogic");
@@ -191,7 +192,7 @@ public class ChooseCardAi extends SpellAbilityAi {
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
return false;
}
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
}
});
@@ -233,7 +234,7 @@ public class ChooseCardAi extends SpellAbilityAi {
return false;
}
for (SpellAbility sa : c.getAllSpellAbilities()) {
if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
if (sa.getPayCosts().hasTapCost()) {
return false;
}
}

View File

@@ -1,6 +1,7 @@
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -23,7 +24,6 @@ public class ChooseCardNameAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
Card source = sa.getHostCard();
if (sa.hasParam("AILogic")) {
// Don't tap creatures that may be able to block
if (ComputerUtil.waitForBlocking(sa)) {
@@ -60,7 +60,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return ComputerUtilCard.getBestAI(options);
}
@@ -86,7 +86,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
if (rules.getSplitType() == CardSplitType.Split) {
Card copy = CardUtil.getLKICopy(card);
// for calcing i need only one split side
// for calcing i need only one split side
if (isOther) {
copy.getCurrentState().copyFrom(card.getState(CardStateName.RightSplit), true);
} else {

View File

@@ -8,10 +8,7 @@ import forge.ai.SpecialCardAi;
import forge.ai.SpellAbilityAi;
import forge.card.MagicColor;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.*;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
@@ -69,9 +66,7 @@ public class ChooseColorAi extends SpellAbilityAi {
}
}
return false;
}
if (logic.equals("MostProminentInComputerDeck")) {
} else if (logic.equals("MostProminentInComputerDeck")) {
if ("Astral Cornucopia".equals(sourceName)) {
// activate in Main 2 hoping that the extra mana surplus will make a difference
// if there are some nonland permanents in hand
@@ -80,6 +75,11 @@ public class ChooseColorAi extends SpellAbilityAi {
return permanents.size() > 0 && ph.is(PhaseType.MAIN2, ai);
}
} else if (logic.equals("HighestDevotionToColor")) {
// currently only works more or less reliably in Main2 to cast own spells
if (!ph.is(PhaseType.MAIN2, ai)) {
return false;
}
}
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());

View File

@@ -0,0 +1,29 @@
package forge.ai.ability;
import com.google.common.collect.Lists;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class ChooseCompanionAi extends SpellAbilityAi {
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
List<Card> cards = Lists.newArrayList(options);
if (cards.isEmpty()) {
return null;
}
Collections.shuffle(cards);
return cards.get(0);
}
}

View File

@@ -46,6 +46,6 @@ public class ChooseDirectionAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
return canPlayAI(ai, sa);
return mandatory || canPlayAI(ai, sa);
}
}

View File

@@ -0,0 +1,36 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.util.MyRandom;
public class ChooseEvenOddAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
if (!sa.hasParam("AILogic")) {
return false;
}
TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
sa.resetTargets();
Player opp = aiPlayer.getWeakestOpponent();
if (sa.canTarget(opp)) {
sa.getTargets().add(opp);
} else {
return false;
}
}
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
return chance;
}
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
return mandatory || canPlayAI(ai, sa);
}
}

View File

@@ -180,25 +180,25 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
Card imprinted = host.getImprintedCards().getFirst();
int dmg = imprinted.getCMC();
Player owner = imprinted.getOwner();
//useless cards in hand
if (imprinted.getName().equals("Bridge from Below") ||
imprinted.getName().equals("Haakon, Stromgald Scourge")) {
return allow;
}
//bad cards when are thrown from the library to the graveyard, but Yixlid can prevent that
if (!player.getGame().isCardInPlay("Yixlid Jailer") && (
imprinted.getName().equals("Gaea's Blessing") ||
imprinted.getName().equals("Narcomoeba"))) {
return allow;
}
// milling against Tamiyo is pointless
if (owner.isCardInCommand("Emblem - Tamiyo, the Moon Sage")) {
return allow;
}
// milling a land against Gitrog result in card draw
if (imprinted.isLand() && owner.isCardInPlay("The Gitrog Monster")) {
// try to mill owner
@@ -207,19 +207,19 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
}
return allow;
}
// milling a creature against Sidisi result in more creatures
if (imprinted.isCreature() && owner.isCardInPlay("Sidisi, Brood Tyrant")) {
return allow;
}
//if Iona does prevent from casting, allow it to draw
//if Iona does prevent from casting, allow it to draw
for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) {
if (CardUtil.getColors(imprinted).hasAnyColor(MagicColor.fromName(io.getChosenColor()))) {
return allow;
}
}
if (dmg == 0) {
// If CMC = 0, mill it!
return deny;
@@ -244,7 +244,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
SpellAbility counterSA = spells.get(0), tokenSA = spells.get(1);
// check for something which might prevent the counters to be placed on host
if (!host.canReceiveCounters(CounterType.P1P1)) {
if (!host.canReceiveCounters(CounterEnumType.P1P1)) {
return tokenSA;
}
@@ -256,7 +256,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
// need a copy for one with extra +1/+1 counter boost,
// without causing triggers to run
final Card copy = CardUtil.getLKICopy(host);
copy.setCounters(CounterType.P1P1, copy.getCounters(CounterType.P1P1) + n);
copy.setCounters(CounterEnumType.P1P1, copy.getCounters(CounterEnumType.P1P1) + n);
copy.setZone(host.getZone());
// if host would put into the battlefield attacking
@@ -281,10 +281,10 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
// TODO check for trigger to turn token ETB into +1/+1 counter for host
// TODO check for trigger to turn token ETB into damage or life loss for opponent
// in this cases Token might be prefered even if they would not survive
final Card tokenCard = TokenAi.spawnToken(player, tokenSA, true);
final Card tokenCard = TokenAi.spawnToken(player, tokenSA);
// Token would not survive
if (tokenCard.getNetToughness() < 1) {
// Token would not survive
if (!tokenCard.isCreature() || tokenCard.getNetToughness() < 1) {
return counterSA;
}
@@ -336,7 +336,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
filtered.add(sp);
}
}
// TODO find better way to check
if (!filtered.isEmpty()) {
return filtered.get(0);
@@ -362,7 +362,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
game.getAction().checkStaticAbilities(false);
// can't gain counters, use Haste
if (!copy.canReceiveCounters(CounterType.P1P1)) {
if (!copy.canReceiveCounters(CounterEnumType.P1P1)) {
return true;
}

View File

@@ -9,6 +9,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class ChoosePlayerAi extends SpellAbilityAi {
@Override
@@ -27,7 +28,7 @@ public class ChoosePlayerAi extends SpellAbilityAi {
}
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices, Map<String, Object> params) {
Player chosen = null;
if ("Curse".equals(sa.getParam("AILogic"))) {
for (Player pc : choices) {

View File

@@ -1,6 +1,7 @@
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
@@ -126,7 +127,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
@Override
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
if ("NeedsPrevention".equals(sa.getParam("AILogic"))) {
final Player ai = sa.getActivatingPlayer();
final Game game = ai.getGame();

View File

@@ -1,6 +1,8 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.collect.Iterables;
import forge.ai.ComputerUtilCard;
@@ -56,7 +58,7 @@ public class ClashAi extends SpellAbilityAi {
* forge.game.spellability.SpellAbility, java.lang.Iterable)
*/
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
for (Player p : options) {
if (p.getCardsIn(ZoneType.Library).size() == 0)
return p;
@@ -82,7 +84,7 @@ public class ClashAi extends SpellAbilityAi {
PlayerCollection players = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
// use chooseSinglePlayer function to the select player
Player chosen = chooseSinglePlayer(ai, sa, players);
Player chosen = chooseSinglePlayer(ai, sa, players, null);
if (chosen != null) {
sa.resetTargets();
sa.getTargets().add(chosen);

View File

@@ -15,6 +15,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class CloneAi extends SpellAbilityAi {
@@ -169,7 +170,7 @@ public class CloneAi extends SpellAbilityAi {
*/
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer) {
Player targetedPlayer, Map<String, Object> params) {
final Card host = sa.getHostCard();
final Player ctrl = host.getController();

View File

@@ -18,6 +18,7 @@
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
@@ -34,12 +35,12 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerCollection;
import forge.game.player.PlayerPredicates;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.collect.FCollectionView;
//AB:GainControl|ValidTgts$Creature|TgtPrompt$Select target legendary creature|LoseControl$Untap,LoseControl|SpellDescription$Gain control of target xxxxxxx
@@ -54,8 +55,6 @@ import forge.util.collect.FCollectionView;
// (as a "&"-separated list; like Haste, Sacrifice CARDNAME at EOT, any standard keyword)
// OppChoice - set to True if opponent chooses creature (for Preacher) - not implemented yet
// Untap - set to True if target card should untap when control is taken
// DestroyTgt - actions upon which the tgt should be destroyed. same list as LoseControl
// NoRegen - set if destroyed creature can't be regenerated. used only with DestroyTgt
/**
* <p>
@@ -77,7 +76,7 @@ public class ControlGainAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Game game = ai.getGame();
final FCollectionView<Player> opponents = ai.getOpponents();
final PlayerCollection opponents = ai.getOpponents();
// if Defined, then don't worry about targeting
if (tgt == null) {
@@ -94,18 +93,19 @@ public class ControlGainAi extends SpellAbilityAi {
sa.setTargetingPlayer(targetingPlayer);
return targetingPlayer.getController().chooseTargetsFor(sa);
}
if (tgt.isRandomTarget()) {
sa.getTargets().add(Aggregates.random(tgt.getAllCandidates(sa, false)));
}
if (tgt.canOnlyTgtOpponent()) {
List<Player> oppList = Lists
.newArrayList(Iterables.filter(opponents, PlayerPredicates.isTargetableBy(sa)));
List<Player> oppList = opponents.filter(PlayerPredicates.isTargetableBy(sa));
if (oppList.isEmpty()) {
return false;
}
sa.getTargets().add(oppList.get(0));
if (tgt.isRandomTarget()) {
sa.getTargets().add(Aggregates.random(oppList));
} else {
sa.getTargets().add(oppList.get(0));
}
}
}
@@ -303,7 +303,7 @@ public class ControlGainAi extends SpellAbilityAi {
} // pumpDrawbackAI()
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
final List<Card> cards = Lists.newArrayList();
for (Player p : options) {
cards.addAll(p.getCreaturesInPlay());

View File

@@ -0,0 +1,82 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
/**
* <p>
* AbilityFactory_GainControlVariant class.
* </p>
*
* @author Forge
* @version $Id: AbilityFactoryGainControl.java 17764 2012-10-29 11:04:18Z Sloth $
*/
public class ControlGainVariantAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
String logic = sa.getParam("AILogic");
if ("GainControlOwns".equals(logic)) {
List<Card> list = CardLists.filter(ai.getGame().getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
@Override
public boolean apply(final Card crd) {
return crd.isCreature() && !crd.getController().equals(crd.getOwner());
}
});
if (list.isEmpty()) {
return false;
}
for (final Card c : list) {
if (ai.equals(c.getController())) {
return false;
}
}
}
return true;
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
Iterable<Card> otherCtrl = CardLists.filter(options, Predicates.not(CardPredicates.isController(ai)));
if (Iterables.isEmpty(otherCtrl)) {
return ComputerUtilCard.getWorstAI(options);
} else {
return ComputerUtilCard.getBestAI(otherCtrl);
}
}
}

View File

@@ -5,6 +5,7 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.ai.*;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.card.*;
import forge.game.card.CardPredicates.Presets;
@@ -18,6 +19,7 @@ import forge.game.zone.ZoneType;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class CopyPermanentAi extends SpellAbilityAi {
@Override
@@ -180,6 +182,13 @@ public class CopyPermanentAi extends SpellAbilityAi {
// if no targeting, it should always be ok
}
if ("TriggeredCardController".equals(sa.getParam("Controller"))) {
Card trigCard = (Card)sa.getTriggeringObject(AbilityKey.Card);
if (!mandatory && trigCard != null && trigCard.getController().isOpponentOf(aiPlayer)) {
return false;
}
}
return true;
}
@@ -196,7 +205,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Select a card to attach to
CardCollection betterOptions = getBetterOptions(ai, sa, options, isOptional);
if (!betterOptions.isEmpty()) {
@@ -215,7 +224,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
}
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
final List<Card> cards = new PlayerCollection(options).getCreaturesInPlay();
Card chosen = ComputerUtilCard.getBestCreatureAI(cards);
return chosen != null ? chosen.getController() : Iterables.getFirst(options, null);

View File

@@ -29,8 +29,8 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
final SpellAbility top = game.getStack().peekAbility();
if (top != null
&& top.getPayCosts() != null && top.getPayCosts().getCostMana() != null
&& sa.getPayCosts() != null && sa.getPayCosts().getCostMana() != null
&& top.getPayCosts().getCostMana() != null
&& sa.getPayCosts().getCostMana() != null
&& top.getPayCosts().getCostMana().getMana().getCMC() >= sa.getPayCosts().getCostMana().getMana().getCMC() + diff) {
// The copied spell has a significantly higher CMC than the copy spell, consider copying
chance = 100;

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -26,7 +26,7 @@ import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.keyword.Keyword;
import forge.util.Aggregates;
@@ -35,7 +35,7 @@ import forge.util.Aggregates;
* <p>
* AbilityFactory_Counters class.
* </p>
*
*
* @author Forge
* @version $Id$
*/
@@ -46,7 +46,7 @@ public abstract class CountersAi {
* <p>
* chooseCursedTarget.
* </p>
*
*
* @param list
* a {@link forge.CardList} object.
* @param type
@@ -77,7 +77,7 @@ public abstract class CountersAi {
* <p>
* chooseBoonTarget.
* </p>
*
*
* @param list
* a {@link forge.CardList} object.
* @param type
@@ -97,7 +97,7 @@ public abstract class CountersAi {
final CardCollection boon = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getCounters(CounterType.DIVINITY) == 0;
return c.getCounters(CounterEnumType.DIVINITY) == 0;
}
});
choice = ComputerUtilCard.getMostExpensivePermanentAI(boon, null, false);

View File

@@ -42,14 +42,14 @@ public class CountersMoveAi extends SpellAbilityAi {
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
final Card host = sa.getHostCard();
final String type = sa.getParam("CounterType");
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
final CounterType cType = "Any".equals(type) ? null : CounterType.getType(type);
// Don't tap creatures that may be able to block
if (ComputerUtil.waitForBlocking(sa)) {
return false;
}
if (CounterType.P1P1.equals(cType) && sa.hasParam("Source")) {
if (CounterEnumType.P1P1.equals(cType) && sa.hasParam("Source")) {
int amount = calcAmount(sa, cType);
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
if (ph.getPlayerTurn().isOpponentOf(ai)) {
@@ -92,7 +92,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// for Simic Fluxmage and other
return ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN);
} else if (CounterType.P1P1.equals(cType) && sa.hasParam("Defined")) {
} else if (CounterEnumType.P1P1.equals(cType) && sa.hasParam("Defined")) {
// something like Cyptoplast Root-kin
if (ph.getPlayerTurn().isOpponentOf(ai)) {
if (ph.inCombat() && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
@@ -115,6 +115,7 @@ public class CountersMoveAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) {
sa.resetTargets();
if (!moveTgtAI(ai, sa) && !mandatory) {
return false;
@@ -142,7 +143,7 @@ public class CountersMoveAi extends SpellAbilityAi {
final Card host = sa.getHostCard();
final String type = sa.getParam("CounterType");
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
final CounterType cType = "Any".equals(type) ? null : CounterType.getType(type);
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
final List<Card> destCards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
@@ -189,7 +190,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// check for some specific AI preferences
if ("DontMoveCounterIfLethal".equals(sa.getParam("AILogic"))) {
return cType != CounterType.P1P1 || src.getNetToughness() - src.getTempToughnessBoost() - 1 > 0;
return !cType.is(CounterEnumType.P1P1) || src.getNetToughness() - src.getTempToughnessBoost() - 1 > 0;
}
}
// no target
@@ -234,7 +235,7 @@ public class CountersMoveAi extends SpellAbilityAi {
final Card host = sa.getHostCard();
final Game game = ai.getGame();
final String type = sa.getParam("CounterType");
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
final CounterType cType = "Any".equals(type) || "All".equals(type) ? null : CounterType.getType(type);
List<Card> tgtCards = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
@@ -278,7 +279,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// do not steal a P1P1 from Undying if it would die
// this way
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
}
return true;
@@ -321,13 +322,13 @@ public class CountersMoveAi extends SpellAbilityAi {
}
// try to remove P1P1 from undying or evolve
if (CounterType.P1P1.equals(cType)) {
if (CounterEnumType.P1P1.equals(cType)) {
if (card.hasKeyword(Keyword.UNDYING) || card.hasKeyword(Keyword.EVOLVE)
|| card.hasKeyword(Keyword.ADAPT)) {
return true;
}
}
if (CounterType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
if (CounterEnumType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
return true;
}
@@ -382,10 +383,10 @@ public class CountersMoveAi extends SpellAbilityAi {
}
if (cType != null) {
if (CounterType.P1P1.equals(cType) && card.hasKeyword(Keyword.UNDYING)) {
if (CounterEnumType.P1P1.equals(cType) && card.hasKeyword(Keyword.UNDYING)) {
return false;
}
if (CounterType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
if (CounterEnumType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
return false;
}
@@ -393,7 +394,7 @@ public class CountersMoveAi extends SpellAbilityAi {
return false;
}
}
return false;
return true;
}
});
@@ -452,7 +453,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// or for source -> multiple defined
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer) {
Player targetedPlayer, Map<String, Object> params) {
if (sa.hasParam("AiLogic")) {
String logic = sa.getParam("AiLogic");

View File

@@ -16,6 +16,7 @@ import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
@@ -77,7 +78,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
final CounterType counterType = getCounterType(sa);
if (!CounterType.P1P1.equals(counterType) && counterType != null) {
if (!CounterEnumType.P1P1.equals(counterType) && counterType != null) {
if (!sa.hasParam("ActivationPhases")) {
// Don't use non P1P1/M1M1 counters before main 2 if possible
if (ph.getPhase().isBefore(PhaseType.MAIN2) && !ComputerUtil.castSpellInMain1(ai, sa)) {
@@ -147,15 +148,15 @@ public class CountersMultiplyAi extends SpellAbilityAi {
if (!aiList.isEmpty()) {
// counter type list to check
// first loyalty, then P1P!, then Charge Counter
List<CounterType> typeList = Lists.newArrayList(CounterType.LOYALTY, CounterType.P1P1, CounterType.CHARGE);
for (CounterType type : typeList) {
List<CounterEnumType> typeList = Lists.newArrayList(CounterEnumType.LOYALTY, CounterEnumType.P1P1, CounterEnumType.CHARGE);
for (CounterEnumType type : typeList) {
// enough targets
if (!sa.canAddMoreTarget()) {
break;
}
if (counterType == null || counterType == type) {
addTargetsByCounterType(ai, sa, aiList, type);
if (counterType == null || counterType.is(type)) {
addTargetsByCounterType(ai, sa, aiList, CounterType.get(type));
}
}
}
@@ -164,7 +165,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
if (!oppList.isEmpty()) {
// not enough targets
if (sa.canAddMoreTarget()) {
final CounterType type = CounterType.M1M1;
final CounterType type = CounterType.get(CounterEnumType.M1M1);
if (counterType == null || counterType == type) {
addTargetsByCounterType(ai, sa, oppList, type);
}

View File

@@ -15,6 +15,7 @@ import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardUtil;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -32,7 +33,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
for (final Player p : allies) {
// player has experience or energy counter
if (p.getCounters(CounterType.EXPERIENCE) + p.getCounters(CounterType.ENERGY) >= 1) {
if (p.getCounters(CounterEnumType.EXPERIENCE) + p.getCounters(CounterEnumType.ENERGY) >= 1) {
allyExpOrEnergy = true;
}
cperms.addAll(CardLists.filter(p.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
@@ -115,17 +116,19 @@ public class CountersProliferateAi extends SpellAbilityAi {
*/
@SuppressWarnings("unchecked")
@Override
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Proliferate is always optional for all, no need to select best
final CounterType poison = CounterType.get(CounterEnumType.POISON);
// because countertype can't be chosen anymore, only look for posion counters
for (final Player p : Iterables.filter(options, Player.class)) {
if (p.isOpponentOf(ai)) {
if (p.getCounters(CounterType.POISON) > 0 && p.canReceiveCounters(CounterType.POISON)) {
if (p.getCounters(poison) > 0 && p.canReceiveCounters(poison)) {
return (T)p;
}
} else {
if (p.getCounters(CounterType.POISON) <= 5 || p.canReceiveCounters(CounterType.POISON)) {
if (p.getCounters(poison) <= 5 || p.canReceiveCounters(poison)) {
return (T)p;
}
}

View File

@@ -35,7 +35,7 @@ public class CountersPutAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#willPayCosts(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.cost.Cost,
* forge.game.card.Card)
@@ -56,17 +56,17 @@ public class CountersPutAi extends SpellAbilityAi {
if (part instanceof CostRemoveCounter) {
final CostRemoveCounter remCounter = (CostRemoveCounter) part;
final CounterType counterType = remCounter.counter;
if (counterType.name().equals(type) && !aiLogic.startsWith("MoveCounter")) {
if (counterType.getName().equals(type) && !aiLogic.startsWith("MoveCounter")) {
return false;
}
if (!part.payCostFromSource()) {
if (counterType.equals(CounterType.P1P1)) {
if (counterType.is(CounterEnumType.P1P1)) {
return false;
}
continue;
}
// don't kill the creature
if (counterType.equals(CounterType.P1P1) && source.getLethalDamage() <= 1) {
if (counterType.is(CounterEnumType.P1P1) && source.getLethalDamage() <= 1) {
return false;
}
}
@@ -77,7 +77,7 @@ public class CountersPutAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
@@ -109,7 +109,7 @@ public class CountersPutAi extends SpellAbilityAi {
}
}
int maxLevel = Integer.parseInt(sa.getParam("MaxLevel"));
return source.getCounters(CounterType.LEVEL) < maxLevel;
return source.getCounters(CounterEnumType.LEVEL) < maxLevel;
}
return super.checkPhaseRestrictions(ai, sa, ph);
@@ -146,7 +146,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (abTgt.canTgtPlayer()) {
// try to kill opponent with Poison
PlayerCollection oppList = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
PlayerCollection poisonList = oppList.filter(PlayerPredicates.hasCounter(CounterType.POISON, 9));
PlayerCollection poisonList = oppList.filter(PlayerPredicates.hasCounter(CounterEnumType.POISON, 9));
if (!poisonList.isEmpty()) {
sa.getTargets().add(poisonList.max(PlayerPredicates.compareByLife()));
return true;
@@ -157,13 +157,13 @@ public class CountersPutAi extends SpellAbilityAi {
// try to kill creature with -1/-1 counters if it can
// receive counters, execpt it has undying
CardCollection oppCreat = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterType.M1M1));
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterEnumType.M1M1));
oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING);
oppCreatM1 = CardLists.filter(oppCreatM1, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
return input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.M1M1);
return input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.get(CounterEnumType.M1M1));
}
});
@@ -220,6 +220,8 @@ public class CountersPutAi extends SpellAbilityAi {
if ("Never".equals(logic)) {
return false;
} else if ("AlwaysWithNoTgt".equals(logic)) {
return true;
} else if ("AristocratCounters".equals(logic)) {
return PumpAi.doAristocratWithCountersLogic(sa, ai);
} else if ("PayEnergy".equals(logic)) {
@@ -242,7 +244,7 @@ public class CountersPutAi extends SpellAbilityAi {
int totBlkPower = Aggregates.sum(blocked, CardPredicates.Accessors.fnGetNetPower);
int totBlkToughness = Aggregates.min(blocked, CardPredicates.Accessors.fnGetNetToughness);
int numActivations = ai.getCounters(CounterType.ENERGY) / sa.getPayCosts().getCostEnergy().convertAmount();
int numActivations = ai.getCounters(CounterEnumType.ENERGY) / sa.getPayCosts().getCostEnergy().convertAmount();
if (sa.getHostCard().getNetToughness() + numActivations > totBlkPower
|| sa.getHostCard().getNetPower() + numActivations >= totBlkToughness) {
return true;
@@ -257,7 +259,7 @@ public class CountersPutAi extends SpellAbilityAi {
AiCardMemory.rememberCard(ai, source, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN);
return true;
}
} else if (ai.getCounters(CounterType.ENERGY) > ComputerUtilCard.getMaxSAEnergyCostOnBattlefield(ai) + sa.getPayCosts().getCostEnergy().convertAmount()) {
} else if (ai.getCounters(CounterEnumType.ENERGY) > ComputerUtilCard.getMaxSAEnergyCostOnBattlefield(ai) + sa.getPayCosts().getCostEnergy().convertAmount()) {
// outside of combat, this logic only works if the relevant AI profile option is enabled
// and if there is enough energy saved
if (!onlyInCombat) {
@@ -291,7 +293,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
return false;
}
if (sourceName.equals("Feat of Resistance")) { // sub-ability should take precedence
CardCollection prot = ProtectAi.getProtectCreatures(ai, sa.getSubAbility());
if (!prot.isEmpty()) {
@@ -320,7 +322,7 @@ public class CountersPutAi extends SpellAbilityAi {
Game game = ai.getGame();
Combat combat = game.getCombat();
if (!source.canReceiveCounters(CounterType.P1P1) || source.getCounters(CounterType.P1P1) > 0) {
if (!source.canReceiveCounters(CounterType.get(CounterEnumType.P1P1)) || source.getCounters(CounterEnumType.P1P1) > 0) {
return false;
} else if (combat != null && ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
return doCombatAdaptLogic(source, amount, combat);
@@ -334,7 +336,7 @@ public class CountersPutAi extends SpellAbilityAi {
}
return FightAi.canFightAi(ai, sa, nPump, nPump);
}
if (amountStr.equals("X")) {
if (source.getSVar(amountStr).equals("Count$xPaid")) {
// By default, set PayX here to maximum value (used for most SAs of this type).
@@ -343,7 +345,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (isClockwork) {
// Clockwork Avian and other similar cards: do not tap all mana for X,
// instead only rewind to max allowed value when the power gets low enough.
int curCtrs = source.getCounters(CounterType.P1P0);
int curCtrs = source.getCounters(CounterEnumType.P1P0);
int maxCtrs = Integer.parseInt(sa.getParam("MaxFromEffect"));
// This will "rewind" clockwork cards when they fall to 50% power or below, consider improving
@@ -394,7 +396,7 @@ public class CountersPutAi extends SpellAbilityAi {
return true;
}
}
if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa)) {
final TargetRestrictions abTgt = sa.getTargetRestrictions();
// only evaluates case where all tokens are placed on a single target
@@ -415,8 +417,8 @@ public class CountersPutAi extends SpellAbilityAi {
// Targeting
if (sa.usesTargeting()) {
sa.resetTargets();
sa.resetTargets();
final boolean sacSelf = ComputerUtilCost.isSacrificeSelfCost(abCost);
if (sa.isCurse()) {
@@ -433,7 +435,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (sacSelf && c.equals(source)) {
return false;
}
return sa.canTarget(c) && c.canReceiveCounters(CounterType.valueOf(type));
return sa.canTarget(c) && c.canReceiveCounters(CounterType.getType(type));
}
});
@@ -452,7 +454,6 @@ public class CountersPutAi extends SpellAbilityAi {
// but try to do it in Main 2 then so that the AI has a chance to play creatures first.
if (list.isEmpty()
&& sa.hasParam("Planeswalker")
&& sa.getPayCosts() != null
&& sa.getPayCosts().hasOnlySpecificCostType(CostPutCounter.class)
&& sa.isTargetNumberValid()
&& sa.getTargets().getNumTargeted() == 0
@@ -487,7 +488,7 @@ public class CountersPutAi extends SpellAbilityAi {
}
return false;
}
// target loop
while (sa.canAddMoreTarget()) {
if (list.isEmpty()) {
@@ -557,7 +558,7 @@ public class CountersPutAi extends SpellAbilityAi {
return false;
}
final int currCounters = cards.get(0).getCounters(CounterType.valueOf(type));
final int currCounters = cards.get(0).getCounters(CounterType.get(type));
// each non +1/+1 counter on the card is a 10% chance of not
// activating this ability.
@@ -573,11 +574,11 @@ public class CountersPutAi extends SpellAbilityAi {
}
boolean immediately = ComputerUtil.playImmediately(ai, sa);
if (abCost != null && !ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa, immediately)) {
return false;
}
if (immediately) {
return true;
}
@@ -611,7 +612,7 @@ public class CountersPutAi extends SpellAbilityAi {
final boolean divided = sa.hasParam("DividedAsYouChoose");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
|| (sa.getRootAbility().isTrigger() && !sa.getRootAbility().isOptionalTrigger());
if (sa.usesTargeting()) {
@@ -691,12 +692,12 @@ public class CountersPutAi extends SpellAbilityAi {
final boolean divided = sa.hasParam("DividedAsYouChoose");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
int left = amount;
if (!sa.usesTargeting()) {
// No target. So must be defined
list = new CardCollection(AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa));
if (amountStr.equals("X")
if (amountStr.equals("X")
&& !source.hasSVar("PayX") /* SubAbility on something that already had set PayX, e.g. Endless One ETB counters */
&& ((sa.hasParam(amountStr) && sa.getSVar(amountStr).equals("Count$xPaid")) || source.getSVar(amountStr).equals("Count$xPaid") )) {
@@ -704,7 +705,7 @@ public class CountersPutAi extends SpellAbilityAi {
SpellAbility testSa = sa;
int countX = 0;
int nonXGlyphs = 0;
while (testSa != null && testSa.getPayCosts() != null && countX == 0) {
while (testSa != null && countX == 0) {
countX = testSa.getPayCosts().getTotalMana().countX();
nonXGlyphs = testSa.getPayCosts().getTotalMana().getGlyphCount() - countX;
testSa = testSa.getSubAbility();
@@ -726,12 +727,27 @@ public class CountersPutAi extends SpellAbilityAi {
source.setSVar("PayX", Integer.toString(payX));
}
if (!mandatory) {
// TODO - If Trigger isn't mandatory, when wouldn't we want to
// put a counter?
// things like Powder Keg, which are way too complex for the AI
}
} else if (sa.getTargetRestrictions().canOnlyTgtOpponent() && !sa.getTargetRestrictions().canTgtCreature()) {
// can only target opponent
List<Player> playerList = Lists.newArrayList(Iterables.filter(
sa.getTargetRestrictions().getAllCandidates(sa, true, true), Player.class));
if (playerList.isEmpty() && mandatory) {
return false;
}
// try to choose player with less creatures
Player choice = Collections.min(playerList, PlayerPredicates.compareByZoneSize(ZoneType.Battlefield, CardPredicates.Presets.CREATURES));
if (choice != null) {
sa.getTargets().add(choice);
}
} else {
if (sa.isCurse()) {
list = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
@@ -838,7 +854,7 @@ public class CountersPutAi extends SpellAbilityAi {
List<Card> threatening = CardLists.filter(creats, new Predicate<Card>() {
@Override
public boolean apply(Card c) {
return CombatUtil.canBlock(source, c, !isHaste)
return CombatUtil.canBlock(source, c, !isHaste)
&& (c.getNetToughness() > source.getNetPower() + tributeAmount || c.hasKeyword(Keyword.DEATHTOUCH));
}
});
@@ -873,24 +889,28 @@ public class CountersPutAi extends SpellAbilityAi {
}
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
// used by Tribute, select player with lowest Life
// TODO add more logic using TributeAILogic
List<Player> list = Lists.newArrayList(options);
return Collections.min(list, PlayerPredicates.compareByLife());
}
@Override
protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Bolster does use this
// TODO need more or less logic there?
final CounterType m1m1 = CounterType.get(CounterEnumType.M1M1);
final CounterType p1p1 = CounterType.get(CounterEnumType.P1P1);
// no logic if there is no options or no to choice
if (!isOptional && Iterables.size(options) <= 1) {
return Iterables.getFirst(options, null);
}
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
final CounterType type = params.containsKey("CounterType") ? (CounterType)params.get("CounterType")
: CounterType.getType(sa.getParam("CounterType"));
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
@@ -907,7 +927,7 @@ public class CountersPutAi extends SpellAbilityAi {
return false;
if (ComputerUtilCard.isUselessCreature(ai, input))
return false;
if (CounterType.M1M1.equals(type) && amount >= input.getNetToughness())
if (type.is(CounterEnumType.M1M1) && amount >= input.getNetToughness())
return true;
return ComputerUtil.isNegativeCounter(type, input);
}
@@ -931,6 +951,20 @@ public class CountersPutAi extends SpellAbilityAi {
CardCollection filtered = mine;
// Try to filter out keywords that we already have on cards
if (type.isKeywordCounter()) {
Keyword kw = Keyword.smartValueOf(type.getName());
final CardCollection doNotHaveKeyword = CardLists.filter(filtered, new Predicate<Card>() {
@Override
public boolean apply(Card card) {
return !card.hasKeyword(kw) && card.canBeTargetedBy(sa) && sa.canTarget(card);
}
});
if (doNotHaveKeyword.size() > 0)
filtered = doNotHaveKeyword;
}
final CardCollection notUseless = CardLists.filter(filtered, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
@@ -945,26 +979,26 @@ public class CountersPutAi extends SpellAbilityAi {
}
// some special logic to reload Persist/Undying
if (CounterType.P1P1.equals(type)) {
if (p1p1.equals(type)) {
final CardCollection persist = CardLists.filter(filtered, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
if (!input.hasKeyword(Keyword.PERSIST))
return false;
return input.getCounters(CounterType.M1M1) <= amount;
return input.getCounters(m1m1) <= amount;
}
});
if (!persist.isEmpty()) {
filtered = persist;
}
} else if (CounterType.M1M1.equals(type)) {
} else if (m1m1.equals(type)) {
final CardCollection undying = CardLists.filter(filtered, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
if (!input.hasKeyword(Keyword.UNDYING))
return false;
return input.getCounters(CounterType.P1P1) <= amount && input.getNetToughness() > amount;
return input.getCounters(p1p1) <= amount && input.getNetToughness() > amount;
}
});
@@ -987,8 +1021,8 @@ public class CountersPutAi extends SpellAbilityAi {
if (e instanceof Card) {
Card c = (Card) e;
if (c.getController().isOpponentOf(ai)) {
if (options.contains(CounterType.M1M1) && !c.hasKeyword(Keyword.UNDYING)) {
return CounterType.M1M1;
if (options.contains(CounterType.get(CounterEnumType.M1M1)) && !c.hasKeyword(Keyword.UNDYING)) {
return CounterType.get(CounterEnumType.M1M1);
}
for (CounterType type : options) {
if (ComputerUtil.isNegativeCounter(type, c)) {
@@ -1005,12 +1039,12 @@ public class CountersPutAi extends SpellAbilityAi {
} else if (e instanceof Player) {
Player p = (Player) e;
if (p.isOpponentOf(ai)) {
if (options.contains(CounterType.POISON)) {
return CounterType.POISON;
if (options.contains(CounterType.get(CounterEnumType.POISON))) {
return CounterType.get(CounterEnumType.POISON);
}
} else {
if (options.contains(CounterType.EXPERIENCE)) {
return CounterType.EXPERIENCE;
if (options.contains(CounterType.get(CounterEnumType.EXPERIENCE))) {
return CounterType.get(CounterEnumType.EXPERIENCE);
}
}

View File

@@ -120,7 +120,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
//Check for cards that could profit from the ability
PhaseHandler phase = ai.getGame().getPhaseHandler();
if (type.equals("P1P1") && sa.isAbility() && source.isCreature()
&& sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()
&& sa.getPayCosts().hasTapCost()
&& sa instanceof AbilitySub
&& (!phase.getNextTurn().equals(ai)
|| phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) {

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -36,7 +36,7 @@ import java.util.Map;
* <p>
* AbilityFactory_PutOrRemoveCountersAi class.
* </p>
*
*
* @author Forge
* @version $Id$
*/
@@ -44,7 +44,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
* forge.game.spellability.SpellAbility)
*/
@@ -75,7 +75,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
if (sa.hasParam("CounterType")) {
// currently only Jhoira's Timebug
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
final CounterType type = CounterType.getType(sa.getParam("CounterType"));
CardCollection countersList = CardLists.filter(list, CardPredicates.hasCounter(type, amount));
@@ -100,7 +100,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
CardCollectionView depthsList = CardLists.filter(countersList,
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterType.ICE));
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterEnumType.ICE));
if (!depthsList.isEmpty()) {
sa.getTargets().add(depthsList.getFirst());
@@ -113,7 +113,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
CardCollection planeswalkerList = CardLists.filter(
CardLists.filterControlledBy(countersList, ai.getOpponents()),
CardPredicates.Presets.PLANESWALKERS,
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
CardPredicates.hasLessCounter(CounterEnumType.LOYALTY, amount));
if (!planeswalkerList.isEmpty()) {
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
@@ -123,7 +123,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
// do as M1M1 part
CardCollection aiList = CardLists.filterControlledBy(countersList, ai);
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1));
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, Keyword.PERSIST);
if (!aiPersistList.isEmpty()) {
@@ -136,7 +136,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
}
// do as P1P1 part
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.P1P1));
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, Keyword.UNDYING);
if (!aiUndyingList.isEmpty()) {
@@ -183,7 +183,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
* forge.game.spellability.SpellAbility, java.util.Map)
*/
@@ -199,18 +199,18 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
Card tgt = (Card) params.get("Target");
// planeswalker has high priority for loyalty counters
if (tgt.isPlaneswalker() && options.contains(CounterType.LOYALTY)) {
return CounterType.LOYALTY;
if (tgt.isPlaneswalker() && options.contains(CounterType.get(CounterEnumType.LOYALTY))) {
return CounterType.get(CounterEnumType.LOYALTY);
}
if (tgt.getController().isOpponentOf(ai)) {
// creatures with BaseToughness below or equal zero might be
// killed if their counters are removed
if (tgt.isCreature() && tgt.getBaseToughness() <= 0) {
if (options.contains(CounterType.P1P1)) {
return CounterType.P1P1;
} else if (options.contains(CounterType.M1M1)) {
return CounterType.M1M1;
if (options.contains(CounterType.get(CounterEnumType.P1P1))) {
return CounterType.get(CounterEnumType.P1P1);
} else if (options.contains(CounterType.get(CounterEnumType.M1M1))) {
return CounterType.get(CounterEnumType.M1M1);
}
}
@@ -222,14 +222,14 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
}
} else {
// this counters are treat first to be removed
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.ICE)) {
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.get(CounterEnumType.ICE))) {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
return CounterType.ICE;
return CounterType.get(CounterEnumType.ICE);
}
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterType.P1P1)) {
return CounterType.P1P1;
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterType.M1M1)) {
return CounterType.M1M1;
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterType.get(CounterEnumType.P1P1))) {
return CounterType.get(CounterEnumType.P1P1);
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterType.get(CounterEnumType.M1M1))) {
return CounterType.get(CounterEnumType.M1M1);
}
// fallback logic, select positive counter to add more
@@ -246,7 +246,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#chooseBinary(forge.game.player.PlayerController.
* BinaryChoiceType, forge.game.spellability.SpellAbility, java.util.Map)
@@ -262,19 +262,19 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
if (tgt.getController().isOpponentOf(ai)) {
if (type.equals(CounterType.LOYALTY) && tgt.isPlaneswalker()) {
if (type.is(CounterEnumType.LOYALTY) && tgt.isPlaneswalker()) {
return false;
}
return ComputerUtil.isNegativeCounter(type, tgt);
} else {
if (type.equals(CounterType.ICE) && "Dark Depths".equals(tgt.getName())) {
if (type.is(CounterEnumType.ICE) && "Dark Depths".equals(tgt.getName())) {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
return false;
}
} else if (type.equals(CounterType.M1M1) && tgt.hasKeyword(Keyword.PERSIST)) {
} else if (type.is(CounterEnumType.M1M1) && tgt.hasKeyword(Keyword.PERSIST)) {
return false;
} else if (type.equals(CounterType.P1P1) && tgt.hasKeyword(Keyword.UNDYING)) {
} else if (type.is(CounterEnumType.P1P1) && tgt.hasKeyword(Keyword.UNDYING)) {
return false;
}

View File

@@ -23,9 +23,17 @@ import java.util.Map;
public class CountersRemoveAi extends SpellAbilityAi {
@Override
protected boolean canPlayWithoutRestrict(final Player ai, final SpellAbility sa) {
if ("Always".equals(sa.getParam("AILogic"))) {
return true;
}
return super.canPlayWithoutRestrict(ai, sa);
}
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
@@ -42,7 +50,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler,
@@ -60,7 +68,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
* forge.game.spellability.SpellAbility)
*/
@@ -74,7 +82,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
if (!type.matches("Any") && !type.matches("All")) {
final int currCounters = sa.getHostCard().getCounters(CounterType.valueOf(type));
final int currCounters = sa.getHostCard().getCounters(CounterType.getType(type));
if (currCounters < 1) {
return false;
}
@@ -111,7 +119,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
CardPredicates.hasCounter(CounterType.ICE, 3));
CardPredicates.hasCounter(CounterEnumType.ICE, 3));
if (!depthsList.isEmpty()) {
sa.getTargets().add(depthsList.getFirst());
@@ -124,7 +132,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
list = CardLists.filter(list, CardPredicates.isTargetableBy(sa));
CardCollection planeswalkerList = CardLists.filter(list, CardPredicates.Presets.PLANESWALKERS,
CardPredicates.hasCounter(CounterType.LOYALTY, 5));
CardPredicates.hasCounter(CounterEnumType.LOYALTY, 5));
if (!planeswalkerList.isEmpty()) {
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
@@ -151,11 +159,11 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
CardPredicates.hasCounter(CounterType.ICE));
CardPredicates.hasCounter(CounterEnumType.ICE));
if (!depthsList.isEmpty()) {
Card depth = depthsList.getFirst();
int ice = depth.getCounters(CounterType.ICE);
int ice = depth.getCounters(CounterEnumType.ICE);
if (amount >= ice) {
sa.getTargets().add(depth);
if (xPay) {
@@ -172,7 +180,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
CardCollection planeswalkerList = CardLists.filter(list,
Predicates.and(CardPredicates.Presets.PLANESWALKERS, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
CardPredicates.hasLessCounter(CounterEnumType.LOYALTY, amount));
if (!planeswalkerList.isEmpty()) {
Card best = ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList);
@@ -188,7 +196,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// do as M1M1 part
CardCollection aiList = CardLists.filterControlledBy(list, ai);
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1));
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, Keyword.PERSIST);
if (!aiPersistList.isEmpty()) {
@@ -201,7 +209,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
// do as P1P1 part
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasLessCounter(CounterType.P1P1, amount));
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasLessCounter(CounterEnumType.P1P1, amount));
CardCollection aiUndyingList = CardLists.getKeyword(aiP1P1List, Keyword.UNDYING);
if (!aiUndyingList.isEmpty()) {
@@ -212,7 +220,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// remove P1P1 counters from opposing creatures
CardCollection oppP1P1List = CardLists.filter(list,
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
CardPredicates.hasCounter(CounterType.P1P1));
CardPredicates.hasCounter(CounterEnumType.P1P1));
if (!oppP1P1List.isEmpty()) {
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(oppP1P1List));
return true;
@@ -236,7 +244,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// no special amount for that one yet
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
CardCollection aiList = CardLists.filterControlledBy(list, ai);
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1, amount));
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1, amount));
CardCollection aiPersist = CardLists.getKeyword(aiList, Keyword.PERSIST);
if (!aiPersist.isEmpty()) {
@@ -255,7 +263,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// no special amount for that one yet
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
list = CardLists.filter(list, CardPredicates.hasCounter(CounterType.P1P1, amount));
list = CardLists.filter(list, CardPredicates.hasCounter(CounterEnumType.P1P1, amount));
// currently only logic for Bloodcrazed Hoplite, but add logic for
// targeting ai creatures too
@@ -301,12 +309,12 @@ public class CountersRemoveAi extends SpellAbilityAi {
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
}
CardCollection timeList = CardLists.filter(list, CardPredicates.hasLessCounter(CounterType.TIME, amount));
CardCollection timeList = CardLists.filter(list, CardPredicates.hasLessCounter(CounterEnumType.TIME, amount));
if (!timeList.isEmpty()) {
Card best = ComputerUtilCard.getBestAI(timeList);
int timeCount = best.getCounters(CounterType.TIME);
int timeCount = best.getCounters(CounterEnumType.TIME);
sa.getTargets().add(best);
if (xPay) {
source.setSVar("PayX", Integer.toString(timeCount));
@@ -327,7 +335,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
CardCollection outlastCreats = CardLists.filter(list, CardPredicates.hasKeyword(Keyword.OUTLAST));
if (!outlastCreats.isEmpty()) {
// outlast cards often benefit from having +1/+1 counters, try not to remove last one
CardCollection betterTargets = CardLists.filter(outlastCreats, CardPredicates.hasCounter(CounterType.P1P1, 2));
CardCollection betterTargets = CardLists.filter(outlastCreats, CardPredicates.hasCounter(CounterEnumType.P1P1, 2));
if (!betterTargets.isEmpty()) {
sa.getTargets().add(ComputerUtilCard.getWorstAI(betterTargets));
@@ -355,7 +363,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#chooseNumber(forge.game.player.Player,
* forge.game.spellability.SpellAbility, int, int, java.util.Map)
*/
@@ -369,8 +377,8 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (targetCard.getController().isOpponentOf(player)) {
return !ComputerUtil.isNegativeCounter(type, targetCard) ? max : min;
} else {
if (targetCard.hasKeyword(Keyword.UNDYING) && type == CounterType.P1P1
&& targetCard.getCounters(CounterType.P1P1) >= max) {
if (targetCard.hasKeyword(Keyword.UNDYING) && type.is(CounterEnumType.P1P1)
&& targetCard.getCounters(CounterEnumType.P1P1) >= max) {
return max;
}
@@ -379,9 +387,9 @@ public class CountersRemoveAi extends SpellAbilityAi {
} else if (target instanceof Player) {
Player targetPlayer = (Player) target;
if (targetPlayer.isOpponentOf(player)) {
return !type.equals(CounterType.POISON) ? max : min;
return !type.equals(CounterEnumType.POISON) ? max : min;
} else {
return type.equals(CounterType.POISON) ? max : min;
return type.equals(CounterEnumType.POISON) ? max : min;
}
}
@@ -390,7 +398,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
* forge.game.spellability.SpellAbility, java.util.Map)
*/
@@ -407,7 +415,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (targetCard.getController().isOpponentOf(ai)) {
// if its a Planeswalker try to remove Loyality first
if (targetCard.isPlaneswalker()) {
return CounterType.LOYALTY;
return CounterType.get(CounterEnumType.LOYALTY);
}
for (CounterType type : options) {
if (!ComputerUtil.isNegativeCounter(type, targetCard)) {
@@ -415,10 +423,10 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
}
} else {
if (options.contains(CounterType.M1M1) && targetCard.hasKeyword(Keyword.PERSIST)) {
return CounterType.M1M1;
} else if (options.contains(CounterType.P1P1) && targetCard.hasKeyword(Keyword.UNDYING)) {
return CounterType.P1P1;
if (options.contains(CounterType.get(CounterEnumType.M1M1)) && targetCard.hasKeyword(Keyword.PERSIST)) {
return CounterType.get(CounterEnumType.M1M1);
} else if (options.contains(CounterType.get(CounterEnumType.P1P1)) && targetCard.hasKeyword(Keyword.UNDYING)) {
return CounterType.get(CounterEnumType.P1P1);
}
for (CounterType type : options) {
if (ComputerUtil.isNegativeCounter(type, targetCard)) {
@@ -430,13 +438,13 @@ public class CountersRemoveAi extends SpellAbilityAi {
Player targetPlayer = (Player) target;
if (targetPlayer.isOpponentOf(ai)) {
for (CounterType type : options) {
if (!type.equals(CounterType.POISON)) {
if (!type.equals(CounterEnumType.POISON)) {
return type;
}
}
} else {
for (CounterType type : options) {
if (type.equals(CounterType.POISON)) {
if (type.equals(CounterEnumType.POISON)) {
return type;
}
}

View File

@@ -6,7 +6,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.cost.Cost;
import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType;
@@ -39,7 +39,7 @@ public class DamageAllAi extends SpellAbilityAi {
if (!ai.getGame().getStack().isEmpty()) {
return false;
}
int x = -1;
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
@@ -50,10 +50,9 @@ public class DamageAllAi extends SpellAbilityAi {
x = ComputerUtilMana.determineLeftoverMana(sa, ai);
}
if (damage.equals("ChosenX")) {
x = source.getCounters(CounterType.LOYALTY);
x = source.getCounters(CounterEnumType.LOYALTY);
}
if (x == -1) {
Player bestOpp = determineOppToKill(ai, sa, source, dmg);
if (determineOppToKill(ai, sa, source, dmg) != null) {
// we already know we can kill a player, so go for it
return true;
@@ -138,7 +137,7 @@ public class DamageAllAi extends SpellAbilityAi {
}
int minGain = 200; // The minimum gain in destroyed creatures
if (sa.getPayCosts() != null && sa.getPayCosts().isReusuableResource()) {
if (sa.getPayCosts().isReusuableResource()) {
if (computerList.isEmpty()) {
minGain = 10; // nothing to lose
// no creatures to lose and player can be damaged

View File

@@ -1,6 +1,7 @@
package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ai.*;
@@ -45,9 +46,9 @@ public class DamageDealAi extends DamageAiBase {
if ("MadSarkhanDigDmg".equals(logic)) {
return SpecialCardAi.SarkhanTheMad.considerDig(ai, sa);
}
if (damage.equals("X") && sa.getSVar(damage).equals("Count$ChosenNumber")) {
int energy = ai.getCounters(CounterType.ENERGY);
int energy = ai.getCounters(CounterEnumType.ENERGY);
for (SpellAbility s : source.getSpellAbilities()) {
if ("PayEnergy".equals(s.getParam("AILogic"))) {
energy += AbilityUtils.calculateAmount(source, s.getParam("CounterNum"), sa);
@@ -144,7 +145,7 @@ public class DamageDealAi extends DamageAiBase {
if (sourceName.equals("Crater's Claws") && ai.hasFerocious()) {
dmg += 2;
}
String logic = sa.getParamOrDefault("AILogic", "");
if ("DiscardLands".equals(logic)) {
dmg = 2;
@@ -164,7 +165,7 @@ public class DamageDealAi extends DamageAiBase {
List<Card> wolves = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), "Creature.Wolf+untapped+YouCtrl+Other", ai, source);
dmg = Aggregates.sum(wolves, CardPredicates.Accessors.fnGetNetPower);
} else if ("Triskelion".equals(logic)) {
final int n = source.getCounters(CounterType.P1P1);
final int n = source.getCounters(CounterEnumType.P1P1);
if (n > 0) {
if (ComputerUtil.playImmediately(ai, sa)) {
/*
@@ -195,9 +196,9 @@ public class DamageDealAi extends DamageAiBase {
}
return false;
}
if (sourceName.equals("Sorin, Grim Nemesis")) {
int loyalty = source.getCounters(CounterType.LOYALTY);
int loyalty = source.getCounters(CounterEnumType.LOYALTY);
for (; loyalty > 0; loyalty--) {
if (this.damageTargetAI(ai, sa, loyalty, false)) {
dmg = ComputerUtilCombat.getEnoughDamageToKill(sa.getTargetCard(), loyalty, source, false, false);
@@ -227,7 +228,7 @@ public class DamageDealAi extends DamageAiBase {
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
return false;
}
if ("DiscardLands".equals(sa.getParam("AILogic")) && !ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
return false;
}
@@ -284,7 +285,7 @@ public class DamageDealAi extends DamageAiBase {
}
}
if ("XCountersDamage".equals(logic) && sa.getPayCosts() != null) {
if ("XCountersDamage".equals(logic)) {
// Check to ensure that we have enough counters to remove per the defined PayX
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
@@ -308,7 +309,7 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* dealDamageChooseTgtC.
* </p>
*
*
* @param d
* a int.
* @param noPrevention
@@ -444,11 +445,11 @@ public class DamageDealAi extends DamageAiBase {
// As of right now, ranks planeswalkers by their Current Loyalty * 10 + Big buff if close to "Ultimate"
int bestScore = 0;
for (Card pw : pws) {
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
int pwScore = curLoyalty * 10;
for (SpellAbility sa : pw.getSpellAbilities()) {
if (sa.hasParam("Ultimate") && sa.getPayCosts() != null) {
if (sa.hasParam("Ultimate")) {
Integer loyaltyCost = 0;
CostRemoveCounter remLoyalty = sa.getPayCosts().getCostPartByType(CostRemoveCounter.class);
if (remLoyalty != null) {
@@ -472,6 +473,22 @@ public class DamageDealAi extends DamageAiBase {
return bestTgt;
}
private Card getWorstPlaneswalkerToDamage(final List<Card> pws) {
Card bestTgt = null;
int bestScore = Integer.MAX_VALUE;
for (Card pw : pws) {
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
if (curLoyalty < bestScore) {
bestScore = curLoyalty;
bestTgt = pw;
}
}
return bestTgt;
}
private List<Card> getTargetableCards(Player ai, SpellAbility sa, Player pl, TargetRestrictions tgt, Player activator, Card source, Game game) {
List<Card> hPlay = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), activator, source, sa);
@@ -498,7 +515,7 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* damageTargetAI.
* </p>
*
*
* @param saMe
* a {@link forge.game.spellability.SpellAbility} object.
* @param dmg
@@ -526,7 +543,7 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* damageChoosingTargets.
* </p>
*
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param tgt
@@ -556,6 +573,13 @@ public class DamageDealAi extends DamageAiBase {
sa.getTargets().add(enemy);
}
return true;
} else if ("DamageAfterPutCounter".equals(logic)
&& sa.getParent() != null
&& "P1P1".equals(sa.getParent().getParam("CounterType"))) {
// assuming the SA parent is of PutCounter type. Perhaps it's possible to predict counter multipliers here somehow?
final String amountStr = sa.getParent().getParamOrDefault("CounterNum", "1");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
dmg += amount;
}
// AssumeAtLeastOneTarget is used for cards with funky targeting implementation like Fight with Fire which would
@@ -563,10 +587,13 @@ public class DamageDealAi extends DamageAiBase {
if (tgt.getMaxTargets(source, sa) <= 0 && !logic.equals("AssumeAtLeastOneTarget")) {
return false;
}
immediately |= ComputerUtil.playImmediately(ai, sa);
sa.resetTargets();
if (!(sa.getParent() != null && sa.getParent().isTargetNumberValid())) {
sa.resetTargets();
}
// target loop
TargetChoices tcs = sa.getTargets();
@@ -596,7 +623,7 @@ public class DamageDealAi extends DamageAiBase {
continue;
}
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(humanCreature, dmg, source, false, noPrevention);
if (assignedDamage <= dmg
if (assignedDamage <= dmg
&& humanCreature.getShieldCount() == 0 && !ComputerUtil.canRegenerate(humanCreature.getController(), humanCreature)) {
tcs.add(humanCreature);
tgt.addDividedAllocation(humanCreature, assignedDamage);
@@ -729,7 +756,7 @@ public class DamageDealAi extends DamageAiBase {
break;
}
}
} else if (tgt.canTgtCreature() || tgt.canTgtPlaneswalker()) {
final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, enemy, mandatory);
if (c != null) {
@@ -767,8 +794,7 @@ public class DamageDealAi extends DamageAiBase {
if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai))
|| (SpellAbilityAi.isSorcerySpeed(sa) && phase.is(PhaseType.MAIN2))
|| ("PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai))
|| sa.getPayCosts() == null || immediately
|| this.shouldTgtP(ai, sa, dmg, noPrevention)) &&
|| immediately || shouldTgtP(ai, sa, dmg, noPrevention)) &&
(!avoidTargetP(ai, sa))) {
tcs.add(enemy);
if (divided) {
@@ -785,7 +811,7 @@ public class DamageDealAi extends DamageAiBase {
return false;
} else {
// If the trigger is mandatory, gotta choose my own stuff now
return this.damageChooseRequiredTargets(ai, sa, tgt, dmg, mandatory);
return this.damageChooseRequiredTargets(ai, sa, tgt, dmg);
}
} else {
// TODO is this good enough? for up to amounts?
@@ -799,8 +825,8 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* damageChooseNontargeted.
* </p>
* @param ai
*
* @param ai
*
* @param saMe
* a {@link forge.game.spellability.SpellAbility} object.
* @param dmg
@@ -855,19 +881,16 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* damageChooseRequiredTargets.
* </p>
*
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param tgt
* a {@link forge.game.spellability.TargetRestrictions} object.
* @param dmg
* a int.
* @param mandatory
* a boolean.
* @return a boolean.
*/
private boolean damageChooseRequiredTargets(final Player ai, final SpellAbility sa, final TargetRestrictions tgt, final int dmg,
final boolean mandatory) {
private boolean damageChooseRequiredTargets(final Player ai, final SpellAbility sa, final TargetRestrictions tgt, final int dmg) {
// this is for Triggered targets that are mandatory
final boolean noPrevention = sa.hasParam("NoPrevention");
final boolean divided = sa.hasParam("DividedAsYouChoose");
@@ -875,7 +898,7 @@ public class DamageDealAi extends DamageAiBase {
while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) {
if (tgt.canTgtPlaneswalker()) {
final Card c = this.dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, ai, mandatory);
final Card c = this.dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, ai, true);
if (c != null) {
sa.getTargets().add(c);
if (divided) {
@@ -888,7 +911,7 @@ public class DamageDealAi extends DamageAiBase {
// TODO: This currently also catches planeswalkers that can be killed (still necessary? Or can be removed?)
if (tgt.canTgtCreature()) {
final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, ai, mandatory);
final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, ai, true);
if (c != null) {
sa.getTargets().add(c);
if (divided) {
@@ -909,6 +932,32 @@ public class DamageDealAi extends DamageAiBase {
}
}
// See if there's an indestructible target that can be used
CardCollection indestructible = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield),
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.Presets.PLANESWALKERS, CardPredicates.hasKeyword(Keyword.INDESTRUCTIBLE), CardPredicates.isTargetableBy(sa)));
if (!indestructible.isEmpty()) {
Card c = ComputerUtilCard.getWorstPermanentAI(indestructible, false, false, false, false);
sa.getTargets().add(c);
if (divided) {
tgt.addDividedAllocation(c, dmg);
break;
}
continue;
}
else if (tgt.canTgtPlaneswalker()) {
// Second pass for planeswalkers: choose AI's worst planeswalker
final Card c = getWorstPlaneswalkerToDamage(CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Predicates.and(CardPredicates.Presets.PLANESWALKERS), CardPredicates.isTargetableBy(sa)));
if (c != null) {
sa.getTargets().add(c);
if (divided) {
tgt.addDividedAllocation(c, dmg);
break;
}
continue;
}
}
if (sa.canTarget(ai)) {
if (sa.getTargets().add(ai)) {
if (divided) {
@@ -957,7 +1006,7 @@ public class DamageDealAi extends DamageAiBase {
// If I can kill my target by paying less mana, do it
int actualPay = 0;
final boolean noPrevention = sa.hasParam("NoPrevention");
//target is a player
if (!sa.getTargets().isTargetingAnyCard()) {
actualPay = dmg;
@@ -988,15 +1037,15 @@ public class DamageDealAi extends DamageAiBase {
Player opponent = ai.getOpponents().min(PlayerPredicates.compareByLife());
// TODO: somehow account for the possible cost reduction?
// TODO: somehow account for the possible cost reduction?
int dmg = ComputerUtilMana.determineLeftoverMana(sa, ai, saTgt.getParam("XColor"));
while (!ComputerUtilMana.canPayManaCost(sa, ai, dmg) && dmg > 0) {
// TODO: ideally should never get here, currently put here as a precaution for complex mana base cases where the miscalculation might occur. Will remove later if it proves to never trigger.
dmg--;
System.out.println("Warning: AI could not pay mana cost for a XLifeDrain logic spell. Reducing X value to "+dmg);
}
// set the color map for black X for the purpose of Soul Burn
// TODO: somehow generalize this calculation to allow other potential similar cards to function in the future
if ("Soul Burn".equals(sourceName)) {
@@ -1017,7 +1066,7 @@ public class DamageDealAi extends DamageAiBase {
int toughness = c.getNetToughness();
boolean canDie = !(c.hasKeyword(Keyword.INDESTRUCTIBLE) || ComputerUtil.canRegenerate(c.getController(), c));
// Currently will target creatures with toughness 3+ (or power 5+)
// Currently will target creatures with toughness 3+ (or power 5+)
// and only if the creature can actually die, do not "underdrain"
// unless the creature has high power
if (canDie && toughness <= dmg && ((toughness == dmg && toughness >= 3) || power >= 5)) {
@@ -1076,8 +1125,7 @@ public class DamageDealAi extends DamageAiBase {
continue;
}
// currently works only with cards that don't have additional costs (only mana is supported)
if (ab.getPayCosts() != null
&& (ab.getPayCosts().hasNoManaCost() || ab.getPayCosts().hasOnlySpecificCostType(CostPartMana.class))) {
if (ab.getPayCosts().hasNoManaCost() || ab.getPayCosts().hasOnlySpecificCostType(CostPartMana.class)) {
String dmgDef = "0";
if (ab.getApi() == ApiType.DealDamage) {
dmgDef = ab.getParamOrDefault("NumDmg", "0");
@@ -1101,7 +1149,7 @@ public class DamageDealAi extends DamageAiBase {
}
// FIXME: should it also check restrictions for targeting players?
ManaCost costSa = sa.getPayCosts() != null ? sa.getPayCosts().getTotalMana() : ManaCost.NO_COST;
ManaCost costSa = sa.getPayCosts().getTotalMana();
ManaCost costAb = ab.getPayCosts().getTotalMana(); // checked for null above
ManaCost total = ManaCost.combine(costSa, costAb);
SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false));

View File

@@ -101,7 +101,7 @@ public class DestroyAi extends SpellAbilityAi {
return SpecialCardAi.SarkhanTheMad.considerMakeDragon(ai, sa);
} else if (logic != null && logic.startsWith("MinLoyalty.")) {
int minLoyalty = Integer.parseInt(logic.substring(logic.indexOf(".") + 1));
if (source.getCounters(CounterType.LOYALTY) < minLoyalty) {
if (source.getCounters(CounterEnumType.LOYALTY) < minLoyalty) {
return false;
}
} else if ("Polymorph".equals(logic)) {
@@ -161,7 +161,7 @@ public class DestroyAi extends SpellAbilityAi {
return false;
}
//Check for undying
return (!c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterType.P1P1) > 0);
return (!c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterEnumType.P1P1) > 0);
}
});
}
@@ -231,7 +231,7 @@ public class DestroyAi extends SpellAbilityAi {
}
if ("Pongify".equals(logic)) {
final Card token = TokenAi.spawnToken(choice.getController(), sa.getSubAbility());
if (token == null) {
if (token == null || !token.isCreature() || token.getNetToughness() < 1) {
return true; // becomes Terminate
} else {
if (source.getGame().getPhaseHandler().getPhase()
@@ -256,7 +256,7 @@ public class DestroyAi extends SpellAbilityAi {
}
//option to hold removal instead only applies for single targeted removal
if (!sa.isTrigger() && abTgt.getMaxTargets(sa.getHostCard(), sa) == 1) {
if (!ComputerUtilCard.useRemovalNow(sa, choice, 0, ZoneType.Graveyard)) {
if (choice == null || !ComputerUtilCard.useRemovalNow(sa, choice, 0, ZoneType.Graveyard)) {
return false;
}
}
@@ -277,6 +277,7 @@ public class DestroyAi extends SpellAbilityAi {
SpellAbility sp = aura.getFirstSpellAbility();
if (sp != null && "GainControl".equals(sp.getParam("AILogic"))
&& aura.getController() != ai && sa.canTarget(aura)) {
list.remove(choice);
choice = aura;
}
}
@@ -387,7 +388,7 @@ public class DestroyAi extends SpellAbilityAi {
if (CardLists.getNotType(list, "Creature").isEmpty()) {
if (!sa.getUniqueTargets().isEmpty() && sa.getParent().getApi() == ApiType.Destroy
&& sa.getUniqueTargets().get(0) instanceof Card) {
// basic ai for Diaochan
// basic ai for Diaochan
c = (Card) sa.getUniqueTargets().get(0);
} else {
c = ComputerUtilCard.getWorstCreatureAI(list);
@@ -412,7 +413,7 @@ public class DestroyAi extends SpellAbilityAi {
Player tgtPlayer = tgtLand.getController();
int oppLandsOTB = tgtPlayer.getLandsInPlay().size();
// AI profile-dependent properties
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
int amountNoTempoCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK);
@@ -435,7 +436,7 @@ public class DestroyAi extends SpellAbilityAi {
// Non-basic lands are currently not ranked in any way in ComputerUtilCard#getBestLandAI, so if a non-basic land is best target,
// consider killing it off unless there's too much potential tempo loss.
// TODO: actually rank non-basics in that method and then kill off the potentially dangerous (manlands, Valakut) or lucrative
// TODO: actually rank non-basics in that method and then kill off the potentially dangerous (manlands, Valakut) or lucrative
// (dual/triple mana that opens access to a certain color) lands
boolean nonBasicTgt = !tgtLand.isBasicLand();
@@ -447,7 +448,7 @@ public class DestroyAi extends SpellAbilityAi {
boolean isHighPriority = highPriorityIfNoLandDrop && oppSkippedLandDrop;
boolean timingCheck = canManaLock || canColorLock || nonBasicTgt;
boolean tempoCheck = numLandsOTB >= amountNoTempoCheck
boolean tempoCheck = numLandsOTB >= amountNoTempoCheck
|| ((numLandsInHand >= amountLandsInHand || isHighPriority) && ((numLandsInHand + numLandsOTB >= amountNoTimingCheck) || timingCheck));
// For Ghost Quarter, only use it if you have either more lands in play than your opponent

View File

@@ -1,6 +1,10 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.ai.*;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
@@ -130,7 +134,7 @@ public class DigAi extends SpellAbilityAi {
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer, Map<String, Object> params) {
if ("DigForCreature".equals(sa.getParam("AILogic"))) {
Card bestChoice = ComputerUtilCard.getBestCreatureAI(valid);
if (bestChoice == null) {
@@ -157,6 +161,15 @@ public class DigAi extends SpellAbilityAi {
}
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List)
*/
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
// an opponent choose a card from
return Iterables.getFirst(options, null);
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
*/

View File

@@ -25,6 +25,7 @@ import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.cost.*;
import forge.game.phase.PhaseHandler;
@@ -262,22 +263,20 @@ public class DrawAi extends SpellAbilityAi {
// Draw up to max hand size but leave at least 3 in library
numCards = Math.min(computerMaxHandSize - computerHandSize, computerLibrarySize - 3);
if (sa.getPayCosts() != null) {
if (sa.getPayCosts().hasSpecificCostType(CostPayLife.class)) {
// [Necrologia, Pay X Life : Draw X Cards]
// Don't draw more than what's "safe" and don't risk a near death experience
// Maybe would be better to check for "serious danger" and take more risk?
while ((ComputerUtil.aiLifeInDanger(ai, false, numCards) && (numCards > 0))) {
numCards--;
}
} else if (sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
// [e.g. Krav, the Unredeemed and other cases which say "Sacrifice X creatures: draw X cards]
// TODO: Add special logic to limit/otherwise modify the ChosenX value here
if (sa.getPayCosts().hasSpecificCostType(CostPayLife.class)) {
// [Necrologia, Pay X Life : Draw X Cards]
// Don't draw more than what's "safe" and don't risk a near death experience
// Maybe would be better to check for "serious danger" and take more risk?
while ((ComputerUtil.aiLifeInDanger(ai, false, numCards) && (numCards > 0))) {
numCards--;
}
} else if (sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
// [e.g. Krav, the Unredeemed and other cases which say "Sacrifice X creatures: draw X cards]
// TODO: Add special logic to limit/otherwise modify the ChosenX value here
// Skip this ability if nothing is to be chosen for sacrifice
if (numCards <= 0) {
return false;
}
// Skip this ability if nothing is to be chosen for sacrifice
if (numCards <= 0) {
return false;
}
}
@@ -350,7 +349,7 @@ public class DrawAi extends SpellAbilityAi {
}
// try to make opponent lose to poison
// currently only Caress of Phyrexia
if (getPoison != null && oppA.canReceiveCounters(CounterType.POISON)) {
if (getPoison != null && oppA.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
if (oppA.getPoisonCounters() + numCards > 9) {
sa.getTargets().add(oppA);
return true;
@@ -394,7 +393,7 @@ public class DrawAi extends SpellAbilityAi {
}
}
if (getPoison != null && ai.canReceiveCounters(CounterType.POISON)) {
if (getPoison != null && ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
if (numCards + ai.getPoisonCounters() >= 8) {
aiTarget = false;
}
@@ -453,7 +452,7 @@ public class DrawAi extends SpellAbilityAi {
}
// ally would lose because of poison
if (getPoison != null && ally.canReceiveCounters(CounterType.POISON)) {
if (getPoison != null && ally.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
if (ally.getPoisonCounters() + numCards > 9) {
continue;
}
@@ -514,6 +513,10 @@ public class DrawAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
if (!mandatory && !willPayCosts(ai, sa, sa.getPayCosts(), sa.getHostCard())) {
return false;
}
return targetAI(ai, sa, mandatory);
}

View File

@@ -11,6 +11,7 @@ import forge.card.mana.ManaCost;
import forge.game.Game;
import forge.game.ability.ApiType;
import forge.game.card.*;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.cost.Cost;
import forge.game.keyword.Keyword;
@@ -112,7 +113,7 @@ public class EffectAi extends SpellAbilityAi {
} else if (logic.equals("SpellCopy")) {
// fetch Instant or Sorcery and AI has reason to play this turn
// does not try to get itself
final ManaCost costSa = sa.getPayCosts() != null ? sa.getPayCosts().getTotalMana() : ManaCost.NO_COST;
final ManaCost costSa = sa.getPayCosts().getTotalMana();
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -134,7 +135,7 @@ public class EffectAi extends SpellAbilityAi {
AiPlayDecision decision = ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(ab);
// see if we can pay both for this spell and for the Effect spell we're considering
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
ManaCost costAb = ab.getPayCosts() != null ? ab.getPayCosts().getTotalMana() : ManaCost.NO_COST;
ManaCost costAb = ab.getPayCosts().getTotalMana();
ManaCost total = ManaCost.combine(costSa, costAb);
SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false));
// can we pay both costs?
@@ -307,6 +308,16 @@ public class EffectAi extends SpellAbilityAi {
if (!ComputerUtil.targetPlayableSpellCard(ai, list, sa, false)) {
return false;
}
} else if (logic.equals("Bribe")) {
Card host = sa.getHostCard();
Combat combat = game.getCombat();
if (combat != null && combat.isAttacking(host, ai) && !combat.isBlocked(host)
&& game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& !AiCardMemory.isRememberedCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN)) {
AiCardMemory.rememberCard(ai, host, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN); // ideally needs once per combat or something
return true;
}
return false;
}
} else { //no AILogic
return false;

View File

@@ -18,6 +18,7 @@
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
@@ -84,7 +85,7 @@ public final class EncodeAi extends SpellAbilityAi {
* forge.game.player.Player)
*/
@Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return chooseCard(ai, options, isOptional);
}

View File

@@ -45,6 +45,8 @@ public class FightAi extends SpellAbilityAi {
aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
List<Card> humCreatures = ai.getOpponents().getCreaturesInPlay();
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
if (humCreatures.isEmpty())
return false; //prevent IndexOutOfBoundsException on MOJHOSTO variant
// assumes the triggered card belongs to the ai
if (sa.hasParam("Defined")) {

View File

@@ -0,0 +1,27 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
public class InvestigateAi extends SpellAbilityAi {
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
*/
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
PhaseHandler ph = aiPlayer.getGame().getPhaseHandler();
return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer;
}
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return true;
}
}

View File

@@ -1,9 +1,11 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.collect.Iterables;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -23,7 +25,7 @@ public class LegendaryRuleAi extends SpellAbilityAi {
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Choose a single legendary/planeswalker card to keep
Card firstOption = Iterables.getFirst(options, null);
boolean choosingFromPlanewalkers = firstOption.isPlaneswalker();
@@ -38,16 +40,16 @@ public class LegendaryRuleAi extends SpellAbilityAi {
if (firstOption.getName().equals("Dark Depths")) {
Card best = firstOption;
for (Card c : options) {
if (c.getCounters(CounterType.ICE) < best.getCounters(CounterType.ICE)) {
if (c.getCounters(CounterEnumType.ICE) < best.getCounters(CounterEnumType.ICE)) {
best = c;
}
}
return best;
} else if (firstOption.getCounters(CounterType.KI) > 0) {
} else if (firstOption.getCounters(CounterEnumType.KI) > 0) {
// Extra Rule for KI counter
Card best = firstOption;
for (Card c : options) {
if (c.getCounters(CounterType.KI) > best.getCounters(CounterType.KI)) {
if (c.getCounters(CounterEnumType.KI) > best.getCounters(CounterEnumType.KI)) {
best = c;
}
}

View File

@@ -88,7 +88,6 @@ public class LifeGainAi extends SpellAbilityAi {
if (lifeCritical
&& sa.isAbility()
&& sa.getHostCard() != null && sa.getHostCard().isCreature()
&& sa.getPayCosts() != null
&& (sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class) || sa.getPayCosts().hasSpecificCostType(CostSacrifice.class))) {
if (!game.getStack().isEmpty()) {
SpellAbility saTop = game.getStack().peekAbility();

View File

@@ -5,7 +5,7 @@ import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -130,7 +130,7 @@ public class LifeSetAi extends SpellAbilityAi {
}
if (sourceName.equals("Eternity Vessel")
&& (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) {
&& (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterEnumType.CHARGE) == 0))) {
return false;
}

View File

@@ -81,7 +81,7 @@ public class ManaEffectAi extends SpellAbilityAi {
return true; // handled elsewhere, does not meet the standard requirements
}
return sa.getPayCosts() != null && sa.getPayCosts().hasNoManaCost() && sa.getPayCosts().isReusuableResource()
return sa.getPayCosts().hasNoManaCost() && sa.getPayCosts().isReusuableResource()
&& sa.getSubAbility() == null && ComputerUtil.playImmediately(ai, sa);
// return super.checkApiLogic(ai, sa);
}
@@ -119,8 +119,8 @@ public class ManaEffectAi extends SpellAbilityAi {
int numCounters = 0;
int manaSurplus = 0;
if ("XChoice".equals(host.getSVar("X"))
&& sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) {
CounterType ctrType = CounterType.KI; // Petalmane Baku
&& sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) {
CounterType ctrType = CounterType.get(CounterEnumType.KI); // Petalmane Baku
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
ctrType = ((CostRemoveCounter)part).counter;

View File

@@ -1,15 +1,10 @@
package forge.ai.ability;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilMana;
import forge.ai.SpecialCardAi;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
@@ -24,6 +19,11 @@ import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
public class MillAi extends SpellAbilityAi {
@Override
@@ -38,6 +38,8 @@ public class MillAi extends SpellAbilityAi {
} else if (aiLogic.equals("LilianaMill")) {
// Only mill if a "Raise Dead" target is available, in case of control decks with few creatures
return CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES).size() >= 1;
} else if (aiLogic.equals("Rebirth")) {
return ai.getLife() <= 8;
}
return true;
}
@@ -194,6 +196,10 @@ public class MillAi extends SpellAbilityAi {
*/
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
if ("TimmerianFiends".equals(sa.getParam("AILogic"))) {
return SpecialCardAi.TimmerianFiends.consider(player, sa);
}
return true;
}

View File

@@ -1,7 +1,7 @@
package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.ai.*;
import forge.game.Game;
@@ -12,14 +12,13 @@ import forge.game.card.CardPredicates;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.keyword.Keyword;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class MustBlockAi extends SpellAbilityAi {
@@ -28,7 +27,6 @@ public class MustBlockAi extends SpellAbilityAi {
final Card source = sa.getHostCard();
final Game game = aiPlayer.getGame();
final Combat combat = game.getCombat();
final PhaseHandler ph = game.getPhaseHandler();
final boolean onlyLethal = !"AllowNonLethal".equals(sa.getParam("AILogic"));
if (combat == null || !combat.isAttacking(source)) {
@@ -39,7 +37,6 @@ public class MustBlockAi extends SpellAbilityAi {
return false;
}
final TargetRestrictions abTgt = sa.getTargetRestrictions();
final List<Card> list = determineGoodBlockers(source, aiPlayer, combat.getDefenderPlayerByAttacker(source), sa, onlyLethal,false);
if (!list.isEmpty()) {
@@ -69,7 +66,6 @@ public class MustBlockAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getHostCard();
final TargetRestrictions abTgt = sa.getTargetRestrictions();
// only use on creatures that can attack
if (!ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
@@ -94,7 +90,7 @@ public class MustBlockAi extends SpellAbilityAi {
boolean chance = false;
if (abTgt != null) {
if (sa.usesTargeting()) {
final List<Card> list = determineGoodBlockers(definedAttacker, ai, ai.getWeakestOpponent(), sa, true,true);
if (list.isEmpty()) {
return false;
@@ -119,6 +115,9 @@ public class MustBlockAi extends SpellAbilityAi {
sa.getTargets().add(blocker);
chance = true;
} else if (sa.hasParam("Choices")) {
// currently choice is attacked player
return true;
} else {
return false;
}
@@ -126,16 +125,9 @@ public class MustBlockAi extends SpellAbilityAi {
return chance;
}
private List<Card> determineGoodBlockers(final Card attacker, final Player ai, Player defender, SpellAbility sa,
private List<Card> determineBlockerFromList(final Card attacker, final Player ai, Iterable<Card> options, SpellAbility sa,
final boolean onlyLethal, final boolean testTapped) {
final Card source = sa.getHostCard();
final TargetRestrictions abTgt = sa.getTargetRestrictions();
List<Card> list = Lists.newArrayList();
list = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
list = CardLists.getTargetableCards(list, sa);
list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source, sa);
list = CardLists.filter(list, new Predicate<Card>() {
List<Card> list = CardLists.filter(options, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
boolean tapped = c.isTapped();
@@ -161,4 +153,40 @@ public class MustBlockAi extends SpellAbilityAi {
return list;
}
private List<Card> determineGoodBlockers(final Card attacker, final Player ai, Player defender, SpellAbility sa,
final boolean onlyLethal, final boolean testTapped) {
List<Card> list = Lists.newArrayList();
list = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES);
if (sa.usesTargeting()) {
list = CardLists.getTargetableCards(list, sa);
}
return determineBlockerFromList(attacker, ai, list, sa, onlyLethal, testTapped);
}
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer, Map<String, Object> params) {
final Card host = sa.getHostCard();
Card attacker = host;
if (sa.hasParam("DefinedAttacker")) {
List<Card> attackers = AbilityUtils.getDefinedCards(host, sa.getParam("DefinedAttacker"), sa);
attacker = Iterables.getFirst(attackers, null);
}
if (attacker == null) {
return Iterables.getFirst(options, null);
}
List<Card> better = determineBlockerFromList(attacker, ai, options, sa, false, false);
if (!better.isEmpty()) {
return Iterables.getFirst(options, null);
}
return Iterables.getFirst(options, null);
}
}

View File

@@ -20,6 +20,7 @@ import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Map;
public class PlayAi extends SpellAbilityAi {
@@ -84,11 +85,11 @@ public class PlayAi extends SpellAbilityAi {
return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost"));
} else if (logic.startsWith("NeedsChosenCard")) {
int minCMC = 0;
if (sa.getPayCosts() != null && sa.getPayCosts().getCostMana() != null) {
minCMC = sa.getPayCosts().getCostMana().getMana().getCMC();
if (sa.getPayCosts().getCostMana() != null) {
minCMC = sa.getPayCosts().getTotalMana().getCMC();
}
validOpts = CardLists.filter(validOpts, CardPredicates.greaterCMC(minCMC));
return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null) != null;
return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null, null) != null;
}
if (source != null && source.hasKeyword(Keyword.HIDEAWAY) && source.hasRemembered()) {
@@ -142,8 +143,7 @@ public class PlayAi extends SpellAbilityAi {
*/
@Override
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options,
final boolean isOptional,
Player targetedPlayer) {
final boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -156,9 +156,7 @@ public class PlayAi extends SpellAbilityAi {
if (sa.hasParam("WithoutManaCost")) {
// Try to avoid casting instants and sorceries with X in their cost, since X will be assumed to be 0.
if (!(spell instanceof SpellPermanent)) {
if (spell.getPayCosts() != null
&& spell.getPayCosts().getCostMana() != null
&& spell.getPayCosts().getCostMana().getMana().countX() > 0) {
if (spell.getPayCosts().getTotalMana().countX() > 0) {
continue;
}
}

View File

@@ -5,6 +5,7 @@ import com.google.common.base.Predicate;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
@@ -59,7 +60,7 @@ public class PoisonAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) {
return tgtPlayer(ai, sa, mandatory);
} else if (mandatory || !ai.canReceiveCounters(CounterType.POISON)) {
} else if (mandatory || !ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
// mandatory or ai is uneffected
return true;
} else {
@@ -92,7 +93,7 @@ public class PoisonAi extends SpellAbilityAi {
public boolean apply(Player input) {
if (input.cantLose()) {
return false;
} else if (!input.canReceiveCounters(CounterType.POISON)) {
} else if (!input.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
return false;
}
return true;
@@ -113,7 +114,7 @@ public class PoisonAi extends SpellAbilityAi {
if (tgts.isEmpty()) {
if (mandatory) {
// AI is uneffected
if (ai.canBeTargetedBy(sa) && ai.canReceiveCounters(CounterType.POISON)) {
if (ai.canBeTargetedBy(sa) && ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
sa.getTargets().add(ai);
return true;
}
@@ -127,7 +128,7 @@ public class PoisonAi extends SpellAbilityAi {
if (input.cantLose()) {
return true;
}
return !input.canReceiveCounters(CounterType.POISON);
return !input.canReceiveCounters(CounterType.get(CounterEnumType.POISON));
}
});

View File

@@ -5,11 +5,7 @@ import java.util.List;
import com.google.common.base.Predicate;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi;
import forge.ai.*;
import forge.card.MagicColor;
import forge.game.Game;
import forge.game.GameObject;
@@ -206,7 +202,7 @@ public class ProtectAi extends SpellAbilityAi {
if (game.getStack().isEmpty()) {
// If the cost is tapping, don't activate before declare
// attack/block
if ((sa.getPayCosts() != null) && sa.getPayCosts().hasTapCost()) {
if (sa.getPayCosts().hasTapCost()) {
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
&& game.getPhaseHandler().isPlayerTurn(ai)) {
list.remove(sa.getHostCard());
@@ -221,6 +217,11 @@ public class ProtectAi extends SpellAbilityAi {
// Don't target cards that will die.
list = ComputerUtil.getSafeTargets(ai, sa, list);
// Don't target self if the cost includes sacrificing itself
if (ComputerUtilCost.isSacrificeSelfCost(sa.getPayCosts())) {
list.remove(source);
}
if (list.isEmpty()) {
return mandatory && protectMandatoryTarget(ai, sa, mandatory);
}

View File

@@ -150,7 +150,7 @@ public class PumpAi extends PumpAiBase {
}
final String counterType = moveSA.getParam("CounterType");
final CounterType cType = "Any".equals(counterType) ? null : CounterType.valueOf(counterType);
final CounterType cType = "Any".equals(counterType) ? null : CounterType.getType(counterType);
final PhaseHandler ph = game.getPhaseHandler();
if (ph.inCombat() && ph.getPlayerTurn().isOpponentOf(ai)) {
@@ -185,7 +185,7 @@ public class PumpAi extends PumpAiBase {
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken();
}
@@ -235,7 +235,7 @@ public class PumpAi extends PumpAiBase {
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken();
}
@@ -402,7 +402,7 @@ public class PumpAi extends PumpAiBase {
if ("DebuffForXCounters".equals(sa.getParam("AILogic")) && sa.getTargetCard() != null) {
// e.g. Skullmane Baku
CounterType ctrType = CounterType.KI;
CounterType ctrType = CounterType.get(CounterEnumType.KI);
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
ctrType = ((CostRemoveCounter)part).counter;
@@ -515,7 +515,7 @@ public class PumpAi extends PumpAiBase {
if (game.getStack().isEmpty()) {
// If the cost is tapping, don't activate before declare
// attack/block
if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
if (sa.getPayCosts().hasTapCost()) {
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
&& game.getPhaseHandler().isPlayerTurn(ai)) {
list.remove(sa.getHostCard());
@@ -730,7 +730,7 @@ public class PumpAi extends PumpAiBase {
final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : "";
if (numDefense.equals("-X") && sa.getSVar("X").equals("Count$ChosenNumber")) {
int energy = ai.getCounters(CounterType.ENERGY);
int energy = ai.getCounters(CounterEnumType.ENERGY);
for (SpellAbility s : source.getSpellAbilities()) {
if ("PayEnergy".equals(s.getParam("AILogic"))) {
energy += AbilityUtils.calculateAmount(source, s.getParam("CounterNum"), sa);
@@ -860,7 +860,7 @@ public class PumpAi extends PumpAiBase {
final boolean isInfect = source.hasKeyword(Keyword.INFECT); // Flesh-Eater Imp
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.POISON)) {
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
lethalDmg = Integer.MAX_VALUE; // won't be able to deal poison damage to kill the opponent
}
@@ -976,7 +976,7 @@ public class PumpAi extends PumpAiBase {
final boolean isInfect = source.hasKeyword(Keyword.INFECT);
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.POISON)) {
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
lethalDmg = Integer.MAX_VALUE; // won't be able to deal poison damage to kill the opponent
}

View File

@@ -94,7 +94,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
List<Card> attackers = CardLists.filter(ai.getCreaturesInPlay(), new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
if (c.equals(sa.getHostCard()) && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()
if (c.equals(sa.getHostCard()) && sa.getPayCosts().hasTapCost()
&& (combat == null || !combat.isAttacking(c))) {
return false;
}
@@ -112,7 +112,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
List<Card> attackers = CardLists.filter(ai.getCreaturesInPlay(), new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
if (c.equals(sa.getHostCard()) && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()
if (c.equals(sa.getHostCard()) && sa.getPayCosts().hasTapCost()
&& (combat == null || !combat.isAttacking(c))) {
return false;
}

View File

@@ -26,7 +26,7 @@ public class RearrangeTopOfLibraryAi extends SpellAbilityAi {
final PhaseHandler ph = aiPlayer.getGame().getPhaseHandler();
final Card source = sa.getHostCard();
if (source.isPermanent() && sa.getRestrictions().isInstantSpeed() && sa.getPayCosts() != null
if (source.isPermanent() && sa.getRestrictions().isInstantSpeed()
&& (sa.getPayCosts().hasTapCost() || sa.getPayCosts().hasManaCost())) {
// If it has an associated cost, try to only do this before own turn
if (!(ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer)) {

View File

@@ -1,6 +1,5 @@
package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard;
import forge.ai.SpecialCardAi;
import forge.ai.SpellAbilityAi;
@@ -16,6 +15,7 @@ import forge.game.zone.ZoneType;
import forge.util.TextUtil;
import java.util.List;
import java.util.Map;
public class RepeatEachAi extends SpellAbilityAi {
@@ -47,21 +47,6 @@ public class RepeatEachAi extends SpellAbilityAi {
return false;
}
}
} else if ("GainControlOwns".equals(logic)) {
List<Card> list = CardLists.filter(aiPlayer.getGame().getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
@Override
public boolean apply(final Card crd) {
return crd.isCreature() && !crd.getController().equals(crd.getOwner());
}
});
if (list.isEmpty()) {
return false;
}
for (final Card c : list) {
if (aiPlayer.equals(c.getController())) {
return false;
}
}
} else if ("OpponentHasCreatures".equals(logic)) {
for (Player opp : aiPlayer.getOpponents()) {
if (!opp.getCreaturesInPlay().isEmpty()){
@@ -118,7 +103,7 @@ public class RepeatEachAi extends SpellAbilityAi {
}
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return ComputerUtilCard.getBestCreatureAI(options);
}
}

View File

@@ -63,6 +63,7 @@ public class SacrificeAi extends SpellAbilityAi {
final boolean destroy = sa.hasParam("Destroy");
Player opp = ai.getWeakestOpponent();
if (tgt != null) {
sa.resetTargets();
if (!opp.canBeTargetedBy(sa)) {
@@ -74,8 +75,16 @@ public class SacrificeAi extends SpellAbilityAi {
num = (num == null) ? "1" : num;
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);
List<Card> list =
CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
List<Card> list = null;
try {
list = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
} catch (NullPointerException e) {
return false;
} finally {
if (list == null)
return false;
}//prevent NPE on MoJhoSto
for (Card c : list) {
if (c.hasSVar("SacMe") && Integer.parseInt(c.getSVar("SacMe")) > 3) {
return false;
@@ -131,15 +140,31 @@ public class SacrificeAi extends SpellAbilityAi {
amount = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), amount);
}
List<Card> humanList =
CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
List<Card> humanList = null;
try {
humanList = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
} catch (NullPointerException e) {
return false;
} finally {
if (humanList == null)
return false;
}//prevent NPE on MoJhoSto
// Since all of the cards have AI:RemoveDeck:All, I enabled 1 for 1
// (or X for X) trades for special decks
return humanList.size() >= amount;
} else if (defined.equals("You")) {
List<Card> computerList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
List<Card> computerList = null;
try {
computerList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);
} catch (NullPointerException e) {
return false;
} finally {
if (computerList == null)
return false;
}//prevent NPE on MoJhoSto
for (Card c : computerList) {
if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) <= 135) {
return true;

View File

@@ -46,12 +46,10 @@ public class ScryAi extends SpellAbilityAi {
// and right before the beginning of AI's turn, if possible, to avoid mana locking the AI and also to
// try to scry right before drawing a card. Also, avoid tapping creatures in the AI's turn, if possible,
// even if there's no mana cost.
if (sa.getPayCosts() != null) {
if (sa.getPayCosts().hasTapCost()
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
&& !SpellAbilityAi.isSorcerySpeed(sa)) {
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
}
if (sa.getPayCosts().hasTapCost()
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
&& !SpellAbilityAi.isSorcerySpeed(sa)) {
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
}
// AI logic to scry in Main 1 if there is no better option, otherwise scry at opponent's EOT
@@ -76,8 +74,7 @@ public class ScryAi extends SpellAbilityAi {
boolean hasSomethingElse = false;
for (Card c : CardLists.filter(ai.getCardsIn(ZoneType.Hand), Predicates.not(CardPredicates.Presets.LANDS))) {
for (SpellAbility ab : c.getAllSpellAbilities()) {
if (ab.getPayCosts() != null
&& ab.getPayCosts().hasManaCost()
if (ab.getPayCosts().hasManaCost()
&& ComputerUtilMana.hasEnoughManaSourcesToCast(ab, ai)) {
// TODO: currently looks for non-Scry cards, can most certainly be made smarter.
if (ab.getApi() != ApiType.Scry) {
@@ -102,7 +99,7 @@ public class ScryAi extends SpellAbilityAi {
} else if ("BrainJar".equals(aiLogic)) {
final Card source = sa.getHostCard();
int counterNum = source.getCounters(CounterType.CHARGE);
int counterNum = source.getCounters(CounterEnumType.CHARGE);
// no need for logic
if (counterNum == 0) {
return false;

View File

@@ -248,7 +248,7 @@ public class SetStateAi extends SpellAbilityAi {
final Card othercard = aiPlayer.getCardsIn(ZoneType.Battlefield, other.getName()).getFirst();
// for legendary KI counter creatures
if (othercard.getCounters(CounterType.KI) >= source.getCounters(CounterType.KI)) {
if (othercard.getCounters(CounterEnumType.KI) >= source.getCounters(CounterEnumType.KI)) {
// if the other legendary is useless try to replace it
return ComputerUtilCard.isUselessCreature(aiPlayer, othercard);
}

View File

@@ -47,12 +47,10 @@ public class SurveilAi extends SpellAbilityAi {
// and right before the beginning of AI's turn, if possible, to avoid mana locking the AI and also to
// try to scry right before drawing a card. Also, avoid tapping creatures in the AI's turn, if possible,
// even if there's no mana cost.
if (sa.getPayCosts() != null) {
if (sa.getPayCosts().hasTapCost()
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
&& !SpellAbilityAi.isSorcerySpeed(sa)) {
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
}
if (sa.getPayCosts().hasTapCost()
&& (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature()))
&& !SpellAbilityAi.isSorcerySpeed(sa)) {
return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN);
}
// in the player's turn Surveil should only be done in Main1 or in Upkeep if able

View File

@@ -3,6 +3,7 @@ package forge.ai.ability;
import forge.ai.*;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.cost.Cost;
import forge.game.cost.CostPart;
@@ -68,7 +69,7 @@ public class TapAi extends TapAiBase {
} else {
if ("TapForXCounters".equals(sa.getParam("AILogic"))) {
// e.g. Waxmane Baku
CounterType ctrType = CounterType.KI;
CounterType ctrType = CounterType.get(CounterEnumType.KI);
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
ctrType = ((CostRemoveCounter)part).counter;

View File

@@ -126,7 +126,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
}
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.isAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
if (sa.isAbility() && sa.getPayCosts().hasTapCost()) {
return true;
}
}
@@ -147,7 +147,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
}
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.isAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
if (sa.isAbility() && sa.getPayCosts().hasTapCost()) {
return true;
}
}

View File

@@ -1,11 +1,12 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.ai.*;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
@@ -25,16 +26,9 @@ import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.zone.ZoneType;
import forge.item.PaperToken;
import forge.util.MyRandom;
import forge.util.TextUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import forge.util.MyRandom;
/**
* <p>
@@ -45,35 +39,10 @@ import java.util.List;
* @version $Id: AbilityFactoryToken.java 17656 2012-10-22 19:32:56Z Max mtg $
*/
public class TokenAi extends SpellAbilityAi {
private String tokenAmount;
private String tokenPower;
private String tokenToughness;
private Card actualToken;
/**
* <p>
* Constructor for AbilityFactory_Token.
* </p>
*
* a {@link forge.game.ability.AbilityFactory} object.
*/
private void readParameters(final SpellAbility mapParams) {
this.tokenAmount = mapParams.getParamOrDefault("TokenAmount", "1");
this.actualToken = TokenInfo.getProtoType(mapParams.getParam("TokenScript"), mapParams);
if (actualToken == null) {
this.tokenPower = mapParams.getParam("TokenPower");
this.tokenToughness = mapParams.getParam("TokenToughness");
} else {
this.tokenPower = actualToken.getBasePowerString();
this.tokenToughness = actualToken.getBaseToughnessString();
}
}
@Override
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
readParameters(sa); // remember to call this somewhere!
final Card source = sa.getHostCard();
// Planeswalker-related flags
boolean pwMinus = false;
@@ -96,21 +65,23 @@ public class TokenAi extends SpellAbilityAi {
}
}
}
String tokenAmount = sa.getParamOrDefault("TokenAmount", "1");
if (actualToken == null) {
actualToken = spawnToken(ai, sa);
}
Card actualToken = spawnToken(ai, sa);
if (actualToken == null) {
if (actualToken == null || actualToken.getNetToughness() < 1) {
final AbilitySub sub = sa.getSubAbility();
// useful
// no token created
return pwPlus || (sub != null && SpellApiToAi.Converter.get(sub.getApi()).chkAIDrawback(sub, ai)); // planeswalker plus ability or sub-ability is
}
String tokenPower = sa.getParamOrDefault("TokenPower", actualToken.getBasePowerString());
String tokenToughness = sa.getParamOrDefault("TokenToughness", actualToken.getBaseToughnessString());
// X-cost spells
if (this.tokenAmount.equals("X") || (this.tokenToughness != null && this.tokenToughness.equals("X"))) {
int x = AbilityUtils.calculateAmount(sa.getHostCard(), this.tokenAmount, sa);
if ("X".equals(tokenAmount) || "X".equals(tokenPower) || "X".equals(tokenToughness)) {
int x = AbilityUtils.calculateAmount(sa.getHostCard(), tokenAmount, sa);
if (source.getSVar("X").equals("Count$Converge")) {
x = ComputerUtilMana.getConvergeCount(sa, ai);
}
@@ -124,14 +95,14 @@ public class TokenAi extends SpellAbilityAi {
}
}
if (canInterruptSacrifice(ai, sa, actualToken)) {
if (canInterruptSacrifice(ai, sa, actualToken, tokenAmount)) {
return true;
}
boolean haste = this.actualToken.hasKeyword(Keyword.HASTE);
boolean haste = actualToken.hasKeyword(Keyword.HASTE);
boolean oneShot = sa.getSubAbility() != null
&& sa.getSubAbility().getApi() == ApiType.DelayedTrigger;
boolean isCreature = this.actualToken.getType().isCreature();
boolean isCreature = actualToken.getType().isCreature();
// Don't generate tokens without haste before main 2 if possible
if (ph.getPhase().isBefore(PhaseType.MAIN2) && ph.isPlayerTurn(ai) && !haste && !sa.hasParam("ActivationPhases")
@@ -166,9 +137,10 @@ public class TokenAi extends SpellAbilityAi {
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false; // prevent infinite tokens?
}
Card actualToken = spawnToken(ai, sa);
// Don't kill AIs Legendary tokens
if (this.actualToken.getType().isLegendary() && ai.isCardInPlay(this.actualToken.getName())) {
if (actualToken.getType().isLegendary() && ai.isCardInPlay(actualToken.getName())) {
// TODO Check if Token is useless due to an aura or counters?
return false;
}
@@ -240,7 +212,7 @@ public class TokenAi extends SpellAbilityAi {
/**
* Checks if the token(s) can save a creature from a sacrifice effect
*/
private boolean canInterruptSacrifice(final Player ai, final SpellAbility sa, final Card token) {
private boolean canInterruptSacrifice(final Player ai, final SpellAbility sa, final Card token, final String tokenAmount) {
final Game game = ai.getGame();
if (game.getStack().isEmpty()) {
return false; // nothing to interrupt
@@ -249,7 +221,7 @@ public class TokenAi extends SpellAbilityAi {
if (topStack.getApi() != ApiType.Sacrifice) {
return false; // not sacrifice effect
}
final int nTokens = AbilityUtils.calculateAmount(sa.getHostCard(), this.tokenAmount, sa);
final int nTokens = AbilityUtils.calculateAmount(sa.getHostCard(), tokenAmount, sa);
final String valid = topStack.getParamOrDefault("SacValid", "Card.Self");
String num = sa.getParam("Amount");
num = (num == null) ? "1" : num;
@@ -271,7 +243,8 @@ public class TokenAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
readParameters(sa);
String tokenAmount = sa.getParamOrDefault("TokenAmount", "1");
final Card source = sa.getHostCard();
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
@@ -282,8 +255,12 @@ public class TokenAi extends SpellAbilityAi {
sa.getTargets().add(ai);
}
}
if ("X".equals(this.tokenAmount) || "X".equals(this.tokenPower) || "X".equals(this.tokenToughness)) {
int x = AbilityUtils.calculateAmount(source, this.tokenAmount, sa);
Card actualToken = spawnToken(ai, sa);
String tokenPower = sa.getParamOrDefault("TokenPower", actualToken.getBasePowerString());
String tokenToughness = sa.getParamOrDefault("TokenToughness", actualToken.getBaseToughnessString());
if ("X".equals(tokenAmount) || "X".equals(tokenPower) || "X".equals(tokenToughness)) {
int x = AbilityUtils.calculateAmount(source, tokenAmount, sa);
if (source.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
x = ComputerUtilMana.determineLeftoverMana(sa, ai);
@@ -321,9 +298,7 @@ public class TokenAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.player.Player> options)
*/
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
// TODO: AILogic
readParameters(sa); // remember to call this somewhere!
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
Combat combat = ai.getGame().getCombat();
// TokenAttacking
if (combat != null && sa.hasParam("TokenAttacking")) {
@@ -341,9 +316,7 @@ public class TokenAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayerOrPlaneswalker(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.GameEntity> options)
*/
@Override
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
// TODO: AILogic
readParameters(sa); // remember to call this somewhere!
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
Combat combat = ai.getGame().getCombat();
// TokenAttacking
if (combat != null && sa.hasParam("TokenAttacking")) {
@@ -374,154 +347,22 @@ public class TokenAi extends SpellAbilityAi {
* @param sa Token SpellAbility
* @return token creature created by ability
*/
@Deprecated
public static Card spawnToken(Player ai, SpellAbility sa) {
return spawnToken(ai, sa, false);
}
/**
* Create the token as a Card object.
* @param ai owner of the new token
* @param sa Token SpellAbility
* @param notNull if the token would not survive, still return it
* @return token creature created by ability
*/
// TODO Is this just completely copied from TokenEffect? Let's just call that thing
@Deprecated
public static Card spawnToken(Player ai, SpellAbility sa, boolean notNull) {
final Card host = sa.getHostCard();
if (!sa.hasParam("TokenScript")) {
throw new RuntimeException("Spell Ability has no TokenScript: " + sa);
}
Card result = TokenInfo.getProtoType(sa.getParam("TokenScript"), sa);
if (result != null) {
result.setController(ai, 0);
return result;
if (result == null) {
throw new RuntimeException("don't find Token for TokenScript: " + sa.getParam("TokenScript"));
}
String[] tokenKeywords = sa.hasParam("TokenKeywords") ? sa.getParam("TokenKeywords").split("<>") : new String[0];
String tokenPower = sa.getParam("TokenPower");
String tokenToughness = sa.getParam("TokenToughness");
String tokenName = sa.getParam("TokenName");
String[] tokenTypes = sa.getParam("TokenTypes").split(",");
StringBuilder cost = new StringBuilder();
String[] tokenColors = sa.getParam("TokenColors").split(",");
String tokenImage = sa.hasParam("TokenImage") ? PaperToken.makeTokenFileName(sa.getParam("TokenImage")) : "";
String[] tokenAbilities = sa.hasParam("TokenAbilities") ? sa.getParam("TokenAbilities").split(",") : null;
String[] tokenTriggers = sa.hasParam("TokenTriggers") ? sa.getParam("TokenTriggers").split(",") : null;
String[] tokenSVars = sa.hasParam("TokenSVars") ? sa.getParam("TokenSVars").split(",") : null;
String[] tokenStaticAbilities = sa.hasParam("TokenStaticAbilities") ? sa.getParam("TokenStaticAbilities").split(",") : null;
String[] tokenHiddenKeywords = sa.hasParam("TokenHiddenKeywords") ? sa.getParam("TokenHiddenKeywords").split("&") : null;
final String[] substitutedColors = Arrays.copyOf(tokenColors, tokenColors.length);
for (int i = 0; i < substitutedColors.length; i++) {
if (substitutedColors[i].equals("ChosenColor")) {
// this currently only supports 1 chosen color
substitutedColors[i] = host.getChosenColor();
}
}
StringBuilder colorDesc = new StringBuilder();
for (final String col : substitutedColors) {
if (col.equalsIgnoreCase("White")) {
colorDesc.append("W ");
} else if (col.equalsIgnoreCase("Blue")) {
colorDesc.append("U ");
} else if (col.equalsIgnoreCase("Black")) {
colorDesc.append("B ");
} else if (col.equalsIgnoreCase("Red")) {
colorDesc.append("R ");
} else if (col.equalsIgnoreCase("Green")) {
colorDesc.append("G ");
} else if (col.equalsIgnoreCase("Colorless")) {
colorDesc = new StringBuilder("C");
}
}
final List<String> imageNames = new ArrayList<>(1);
if (tokenImage.equals("")) {
imageNames.add(PaperToken.makeTokenFileName(TextUtil.fastReplace(colorDesc.toString(), " ", ""), tokenPower, tokenToughness, tokenName));
} else {
imageNames.add(0, tokenImage);
}
result.setOwner(ai);
for (final char c : colorDesc.toString().toCharArray()) {
cost.append(c + ' ');
}
cost = new StringBuilder(colorDesc.toString().replace('C', '1').trim());
final int finalPower = AbilityUtils.calculateAmount(host, tokenPower, sa);
final int finalToughness = AbilityUtils.calculateAmount(host, tokenToughness, sa);
final String[] substitutedTypes = Arrays.copyOf(tokenTypes, tokenTypes.length);
for (int i = 0; i < substitutedTypes.length; i++) {
if (substitutedTypes[i].equals("ChosenType")) {
substitutedTypes[i] = host.getChosenType();
}
}
final String substitutedName = tokenName.equals("ChosenType") ? host.getChosenType() : tokenName;
final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size()));
final TokenInfo tokenInfo = new TokenInfo(substitutedName, imageName,
cost.toString(), substitutedTypes, tokenKeywords, finalPower, finalToughness);
Card token = tokenInfo.makeOneToken(ai);
if (token == null) {
return null;
}
// Grant rule changes
if (tokenHiddenKeywords != null) {
for (final String s : tokenHiddenKeywords) {
token.addHiddenExtrinsicKeyword(s);
}
}
// Grant abilities
if (tokenAbilities != null) {
for (final String s : tokenAbilities) {
final String actualAbility = host.getSVar(s);
final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, token);
token.addSpellAbility(grantedAbility);
}
}
// Grant triggers
if (tokenTriggers != null) {
for (final String s : tokenTriggers) {
final String actualTrigger = host.getSVar(s);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, token, true);
final String ability = host.getSVar(parsedTrigger.getParam("Execute"));
parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(ability, token));
token.addTrigger(parsedTrigger);
}
}
// Grant SVars
if (tokenSVars != null) {
for (final String s : tokenSVars) {
String actualSVar = host.getSVar(s);
String name = s;
if (actualSVar.startsWith("SVar")) {
actualSVar = actualSVar.split("SVar:")[1];
name = actualSVar.split(":")[0];
actualSVar = actualSVar.split(":")[1];
}
token.setSVar(name, actualSVar);
}
}
// Grant static abilities
if (tokenStaticAbilities != null) {
for (final String s : tokenStaticAbilities) {
token.addStaticAbility(host.getSVar(s));
}
}
// Apply static abilities and prune dead tokens
// Apply static abilities
final Game game = ai.getGame();
ComputerUtilCard.applyStaticContPT(game, token, null);
if (!notNull && token.isCreature() && token.getNetToughness() < 1) {
return null;
} else {
return token;
}
ComputerUtilCard.applyStaticContPT(game, result, null);
return result;
}
}

View File

@@ -24,6 +24,7 @@ import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class UntapAi extends SpellAbilityAi {
@Override
@@ -153,12 +154,11 @@ public class UntapAi extends SpellAbilityAi {
// Try to avoid potential infinite recursion,
// e.g. Kiora's Follower untapping another Kiora's Follower and repeating infinitely
if (sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostTap.class)) {
if (sa.getPayCosts().hasOnlySpecificCostType(CostTap.class)) {
CardCollection toRemove = new CardCollection();
for (Card c : untapList) {
for (SpellAbility ab : c.getAllSpellAbilities()) {
if (ab.getApi() == ApiType.Untap
&& ab.getPayCosts() != null
&& ab.getPayCosts().hasOnlySpecificCostType(CostTap.class)
&& ab.canTarget(source)) {
toRemove.add(c);
@@ -312,7 +312,7 @@ public class UntapAi extends SpellAbilityAi {
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> list, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> list, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
PlayerCollection pl = new PlayerCollection();
pl.add(ai);
pl.addAll(ai.getAllies());

View File

@@ -46,6 +46,12 @@ public class VoteAi extends SpellAbilityAi {
@Override
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
if (params.containsKey("Voter")) {
Player p = (Player)params.get("Voter");
if (p.isOpponentOf(player)) {
return min;
}
}
if (sa.getActivatingPlayer().isOpponentOf(player)) {
return min;
}

View File

@@ -76,7 +76,7 @@ public class GameCopier {
newPlayer.addSpellCastThisTurn();
for (int j = 0; j < origPlayer.getLandsPlayedThisTurn(); j++)
newPlayer.addLandPlayedThisTurn();
newPlayer.setCounters(Maps.newEnumMap(origPlayer.getCounters()));
newPlayer.setCounters(Maps.newHashMap(origPlayer.getCounters()));
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
newPlayer.setPreventNextDamage(origPlayer.getPreventNextDamage());
@@ -125,8 +125,6 @@ public class GameCopier {
}
}
}
origGame.validateSpabCache();
newGame.validateSpabCache();
// Undo effects first before calculating them below, to avoid them applying twice.
for (StaticEffect effect : origGame.getStaticEffects().getEffects()) {
@@ -330,7 +328,7 @@ public class GameCopier {
Map<CounterType, Integer> counters = c.getCounters();
if (!counters.isEmpty()) {
newCard.setCounters(Maps.newEnumMap(counters));
newCard.setCounters(Maps.newHashMap(counters));
}
if (c.getChosenPlayer() != null) {
newCard.setChosenPlayer(playerMap.get(c.getChosenPlayer()));

View File

@@ -3,7 +3,7 @@ package forge.ai.simulation;
import forge.ai.CreatureEvaluator;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
@@ -29,6 +29,13 @@ public class GameStateEvaluator {
if (phase.isAfter(PhaseType.COMBAT_DAMAGE) || evalGame.isGameOver()) {
return null;
}
// If the current player has no creatures in play, there won't be any combat. This avoids
// an expensive game copy operation.
// Note: This is is safe to do because the simulation is based on the current game state,
// so there isn't a chance to play creatures in between.
if (evalGame.getPhaseHandler().getPlayerTurn().getCreaturesInPlay().isEmpty()) {
return null;
}
GameCopier copier = new GameCopier(evalGame);
Game gameCopy = copier.makeCopy();
gameCopy.getPhaseHandler().devAdvanceToPhase(PhaseType.COMBAT_DAMAGE);
@@ -147,7 +154,7 @@ public class GameStateEvaluator {
// e.g. a 5 CMC permanent results in 200, whereas a 5/5 creature is ~225
int value = 50 + 30 * c.getCMC();
if (c.isPlaneswalker()) {
value += 2 * c.getCounters(CounterType.LOYALTY);
value += 2 * c.getCounters(CounterEnumType.LOYALTY);
}
return value;
}

View File

@@ -23,6 +23,7 @@ import forge.util.TextUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
public class SpellAbilityPicker {
private Game game;
@@ -307,7 +308,7 @@ public class SpellAbilityPicker {
if (conditions == null) {
return true;
}
List<PhaseType> phases = conditions.getPhases();
Set<PhaseType> phases = conditions.getPhases();
return phases.isEmpty() || phases.contains(PhaseType.MAIN1);
}

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>1.6.30-SNAPSHOT</version>
<version>1.6.35</version>
</parent>
<artifactId>forge-core</artifactId>
@@ -21,7 +21,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
<version>3.8.1</version>
</dependency>
</dependencies>

View File

@@ -6,6 +6,8 @@ import forge.util.ImageUtil;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@@ -111,7 +113,28 @@ public final class ImageKeys {
file = findFile(dir, TextUtil.fastReplace(filename, "AE", "Ae"));
if (file != null) { return file; }
}
//try fullborder...
if (filename.contains(".full")) {
String fullborderFile = TextUtil.fastReplace(filename, ".full", ".fullborder");
file = findFile(dir, fullborderFile);
if (file != null) { return file; }
// if there's a 1st art variant try without it for .fullborder images
file = findFile(dir, TextUtil.fastReplace(fullborderFile, "1.fullborder", ".fullborder"));
if (file != null) { return file; }
// if there's an art variant try without it for .full images
file = findFile(dir, filename.replaceAll("[0-9].full]",".full"));
if (file != null) { return file; }
// if there's a 1st art variant try with it for .full images
file = findFile(dir, filename.replaceAll("[0-9]*.full", "1.full"));
if (file != null) { return file; }
}
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
if (!filename.contains(".full")) {
file = findFile(dir, TextUtil.addSuffix(filename,".full"));
if (file != null) { return file; }
file = findFile(dir, TextUtil.addSuffix(filename,".fullborder"));
if (file != null) { return file; }
}
// some S00 cards are really part of 6ED
String s2kAlias = getSetFolder("S00");
if (filename.startsWith(s2kAlias)) {

View File

@@ -11,6 +11,7 @@ import org.apache.commons.lang3.StringUtils;
public abstract class LobbyPlayer {
protected String name;
private int avatarIndex = -1;
private int sleeveIndex = -1;
private String avatarCardImageKey;
public LobbyPlayer(String name) {
@@ -59,9 +60,15 @@ public abstract class LobbyPlayer {
public int getAvatarIndex() {
return avatarIndex;
}
public int getSleeveIndex() {
return sleeveIndex;
}
public void setAvatarIndex(int avatarIndex) {
this.avatarIndex = avatarIndex;
}
public void setSleeveIndex(int sleeveIndex) {
this.sleeveIndex = sleeveIndex;
}
public String getAvatarCardImageKey() {
return avatarCardImageKey;

View File

@@ -35,6 +35,7 @@ public class StaticData {
private Predicate<PaperCard> standardPredicate;
private Predicate<PaperCard> brawlPredicate;
private Predicate<PaperCard> pioneerPredicate;
private Predicate<PaperCard> modernPredicate;
private Predicate<PaperCard> commanderPredicate;
private Predicate<PaperCard> oathbreakerPredicate;
@@ -53,11 +54,11 @@ public class StaticData {
private static StaticData lastInstance = null;
public StaticData(CardStorageReader cardReader, String editionFolder, String blockDataFolder) {
this(cardReader, null, editionFolder, blockDataFolder);
public StaticData(CardStorageReader cardReader, String editionFolder, String blockDataFolder, boolean enableUnknownCards) {
this(cardReader, null, editionFolder, blockDataFolder, enableUnknownCards);
}
public StaticData(CardStorageReader cardReader, CardStorageReader tokenReader, String editionFolder, String blockDataFolder) {
public StaticData(CardStorageReader cardReader, CardStorageReader tokenReader, String editionFolder, String blockDataFolder, boolean enableUnknownCards) {
this.cardReader = cardReader;
this.tokenReader = tokenReader;
this.editions = new CardEdition.Collection(new CardEdition.Reader(new File(editionFolder)));
@@ -83,8 +84,8 @@ public class StaticData {
variantCards = new CardDb(variantsCards, editions);
//must initialize after establish field values for the sake of card image logic
commonCards.initialize(false, false);
variantCards.initialize(false, false);
commonCards.initialize(false, false, enableUnknownCards);
variantCards.initialize(false, false, enableUnknownCards);
}
{
@@ -197,13 +198,13 @@ public class StaticData {
public TokenDb getAllTokens() { return allTokens; }
public Predicate<PaperCard> getStandardPredicate() {
return standardPredicate;
}
public void setStandardPredicate(Predicate<PaperCard> standardPredicate) { this.standardPredicate = standardPredicate; }
public void setModernPredicate(Predicate<PaperCard> modernPredicate) { this.modernPredicate = standardPredicate; }
public void setPioneerPredicate(Predicate<PaperCard> pioneerPredicate) { this.pioneerPredicate = pioneerPredicate; }
public void setModernPredicate(Predicate<PaperCard> modernPredicate) { this.modernPredicate = modernPredicate; }
public void setCommanderPredicate(Predicate<PaperCard> commanderPredicate) { this.commanderPredicate = commanderPredicate; }
@@ -211,9 +212,11 @@ public class StaticData {
public void setBrawlPredicate(Predicate<PaperCard> brawlPredicate) { this.brawlPredicate = brawlPredicate; }
public Predicate<PaperCard> getModernPredicate() {
return modernPredicate;
}
public Predicate<PaperCard> getStandardPredicate() { return standardPredicate; }
public Predicate<PaperCard> getPioneerPredicate() { return pioneerPredicate; }
public Predicate<PaperCard> getModernPredicate() { return modernPredicate; }
public Predicate<PaperCard> getCommanderPredicate() { return commanderPredicate; }

View File

@@ -165,7 +165,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
reIndex();
}
public void initialize(boolean logMissingPerEdition, boolean logMissingSummary) {
public void initialize(boolean logMissingPerEdition, boolean logMissingSummary, boolean enableUnknownCards) {
Set<String> allMissingCards = new LinkedHashSet<>();
List<String> missingCards = new ArrayList<>();
CardEdition upcomingSet = null;
@@ -218,7 +218,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
if (!contains(cr.getName())) {
if (upcomingSet != null) {
addCard(new PaperCard(cr, upcomingSet.getCode(), CardRarity.Unknown, 1));
} else {
} else if(enableUnknownCards) {
System.err.println("The card " + cr.getName() + " was not assigned to any set. Adding it to UNKNOWN set... to fix see res/editions/ folder. ");
addCard(new PaperCard(cr, CardEdition.UNKNOWN.getCode(), CardRarity.Special, 1));
}
@@ -312,17 +312,21 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
return tryGetCard(request);
}
public int getCardCollectorNumber(String cardName, String reqEdition) {
public String getCardCollectorNumber(String cardName, String reqEdition, int artIndex) {
cardName = getName(cardName);
CardEdition edition = editions.get(reqEdition);
if (edition == null)
return -1;
return null;
int numMatches = 0;
for (CardInSet card : edition.getCards()) {
if (card.name.equalsIgnoreCase(cardName)) {
return card.collectorNumber;
numMatches += 1;
if (numMatches == artIndex) {
return card.collectorNumber;
}
}
}
return -1;
return null;
}
private PaperCard tryGetCard(CardRequest request) {
@@ -553,6 +557,23 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
return Lists.newArrayList(Iterables.filter(this.roAllCards, predicate));
}
// Do I want a foiled version of these cards?
@Override
public List<PaperCard> getAllCardsFromEdition(CardEdition edition) {
List<PaperCard> cards = Lists.newArrayList();
for(CardInSet cis : edition.getCards()) {
PaperCard card = this.getCard(cis.name, edition.getCode());
if (card == null) {
// Just in case the card is listed in the edition file but Forge doesn't support it
continue;
}
cards.add(card);
}
return cards;
}
@Override
public boolean contains(String name) {
return allCardsByName.containsKey(getName(name));

View File

@@ -38,6 +38,8 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@@ -75,10 +77,10 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public static class CardInSet {
public final CardRarity rarity;
public final int collectorNumber;
public final String collectorNumber;
public final String name;
public CardInSet(final String name, final int collectorNumber, final CardRarity rarity) {
public CardInSet(final String name, final String collectorNumber, final CardRarity rarity) {
this.name = name;
this.collectorNumber = collectorNumber;
this.rarity = rarity;
@@ -86,7 +88,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public String toString() {
StringBuilder sb = new StringBuilder();
if (collectorNumber != -1) {
if (collectorNumber != null) {
sb.append(collectorNumber);
sb.append(' ');
}
@@ -110,6 +112,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
private Type type;
private String name;
private String alias = null;
private String prerelease = null;
private boolean whiteBorder = false;
private FoilType foilType = FoilType.NOT_SUPPORTED;
private double foilChanceInBooster = 0;
@@ -178,6 +181,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public Type getType() { return type; }
public String getName() { return name; }
public String getAlias() { return alias; }
public String getPrerelease() { return prerelease; }
public FoilType getFoilType() { return foilType; }
public double getFoilChanceInBooster() { return foilChanceInBooster; }
public boolean getFoilAlwaysInCommonSlot() { return foilAlwaysInCommonSlot; }
@@ -188,6 +192,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public boolean getSmallSetOverride() { return smallSetOverride; }
public String getBoosterMustContain() { return boosterMustContain; }
public CardInSet[] getCards() { return cards; }
public boolean isModern() { return getDate().after(parseDate("2003-07-27")); } //8ED and above are modern except some promo cards and others
public Map<String, Integer> getTokens() { return tokenNormalized; }
@@ -264,24 +269,33 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
Map<String, Integer> tokenNormalized = new HashMap<>();
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
if (contents.containsKey("cards")) {
final Pattern pattern = Pattern.compile(
/*
The following pattern will match the WAR Japanese art entries,
it should also match the Un-set and older alternate art cards
like Merseine from FEM (should the editions files ever be updated)
*/
//"(^(?<cnum>[0-9]+.?) )?((?<rarity>[SCURML]) )?(?<name>.*)$"
/* Ideally we'd use the named group above, but Android 6 and
earlier don't appear to support named groups.
So, untill support for those devices is officially dropped,
we'll have to suffice with numbered groups.
We are looking for:
* cnum - grouping #2
* rarity - grouping #4
* name - grouping #5
*/
"(^([0-9]+.?) )?(([SCURML]) )?(.*)$"
);
for(String line : contents.get("cards")) {
if (StringUtils.isBlank(line))
continue;
// Optional collector number at the start.
String[] split = line.split(" ", 2);
int collectorNumber = -1;
if (split.length >= 2 && StringUtils.isNumeric(split[0])) {
collectorNumber = Integer.parseInt(split[0]);
line = split[1];
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
String collectorNumber = matcher.group(2);
CardRarity r = CardRarity.smartValueOf(matcher.group(4));
String cardName = matcher.group(5);
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
processedCards.add(cis);
}
// You may omit rarity for early development
CardRarity r = CardRarity.smartValueOf(line.substring(0, 1));
boolean hadRarity = r != CardRarity.Unknown && line.charAt(1) == ' ';
String cardName = hadRarity ? line.substring(2) : line;
CardInSet cis = new CardInSet(cardName, collectorNumber, r);
processedCards.add(cis);
}
}
@@ -303,7 +317,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
tokenNormalized
);
FileSection section = FileSection.parse(contents.get("metadata"), "=");
FileSection section = FileSection.parse(contents.get("metadata"), FileSection.EQUALS_KV_SEPARATOR);
res.name = section.get("name");
res.date = parseDate(section.get("date"));
res.code = section.get("code");
@@ -333,6 +347,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
}
}
res.type = enumType;
res.prerelease = section.get("Prerelease", null);
switch(section.get("foil", "newstyle").toLowerCase()) {
case "notsupported":
@@ -413,6 +428,16 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
return res;
}
public Iterable<CardEdition> getPrereleaseEditions() {
List<CardEdition> res = Lists.newArrayList(this);
return Iterables.filter(res, new Predicate<CardEdition>() {
@Override
public boolean apply(final CardEdition edition) {
return edition.getPrerelease() != null;
}
});
}
public CardEdition getEditionByCodeOrThrow(final String code) {
final CardEdition set = this.get(code);
if (null == set) {

View File

@@ -222,7 +222,12 @@ public final class CardRules implements ICardCharacteristics {
public boolean canBeBrawlCommander() {
CardType type = mainPart.getType();
return (type.isLegendary() && type.isCreature()) || type.isPlaneswalker();
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
}
public boolean canBeTinyLeadersCommander() {
CardType type = mainPart.getType();
return type.isLegendary() && (type.isCreature() || type.isPlaneswalker());
}
public String getMeldWith() {
@@ -526,12 +531,10 @@ public final class CardRules implements ICardCharacteristics {
public final ManaCostShard next() {
final String unparsed = st.nextToken();
// System.out.println(unparsed);
try {
int iVal = Integer.parseInt(unparsed);
this.genericCost += iVal;
if (StringUtils.isNumeric(unparsed)) {
this.genericCost += Integer.parseInt(unparsed);
return null;
}
catch (NumberFormatException nex) { }
return ManaCostShard.parseNonGeneric(unparsed);
}

View File

@@ -594,8 +594,10 @@ public final class CardRulesPredicates {
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
public static final Predicate<CardRules> CAN_BE_TINY_LEADERS_COMMANDER = Predicates.and(Presets.IS_LEGENDARY,
Predicates.or(Presets.IS_CREATURE, Presets.IS_PLANESWALKER));
/** The Constant IS_NON_CREATURE_SPELL. **/
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates

View File

@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
@@ -35,10 +36,8 @@ import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import forge.util.EnumUtil;
import forge.util.Settable;
/**
@@ -47,7 +46,6 @@ import forge.util.Settable;
* </p>
*
* @author Forge
* @version $Id: java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class CardType implements Comparable<CardType>, CardTypeView {
private static final long serialVersionUID = 4629853583167022151L;
@@ -71,7 +69,16 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
Vanguard(false);
public final boolean isPermanent;
private static final ImmutableList<String> allCoreTypeNames = EnumUtil.getNames(CoreType.class);
private static Map<String, CoreType> stringToCoreType = EnumUtils.getEnumMap(CoreType.class);
private static final Set<String> allCoreTypeNames = stringToCoreType.keySet();
public static CoreType getEnum(String name) {
return stringToCoreType.get(name);
}
public static boolean isValidEnum(String name) {
return stringToCoreType.containsKey(name);
}
CoreType(final boolean permanent) {
isPermanent = permanent;
@@ -86,19 +93,17 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
Ongoing,
World;
private static final ImmutableList<String> allSuperTypeNames = EnumUtil.getNames(Supertype.class);
}
private static Map<String, Supertype> stringToSupertype = EnumUtils.getEnumMap(Supertype.class);
private static final Set<String> allSuperTypeNames = stringToSupertype.keySet();
// This will be useful for faster parses
private static Map<String, CoreType> stringToCoreType = Maps.newHashMap();
private static Map<String, Supertype> stringToSupertype = Maps.newHashMap();
static {
for (final Supertype st : Supertype.values()) {
stringToSupertype.put(st.name(), st);
public static Supertype getEnum(String name) {
return stringToSupertype.get(name);
}
for (final CoreType ct : CoreType.values()) {
stringToCoreType.put(ct.name(), ct);
public static boolean isValidEnum(String name) {
return stringToSupertype.containsKey(name);
}
}
private final Set<CoreType> coreTypes = EnumSet.noneOf(CoreType.class);
@@ -120,12 +125,12 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
public boolean add(final String t) {
boolean changed;
final CoreType ct = stringToCoreType.get(t);
final CoreType ct = CoreType.getEnum(t);
if (ct != null) {
changed = coreTypes.add(ct);
}
else {
final Supertype st = stringToSupertype.get(t);
final Supertype st = Supertype.getEnum(t);
if (st != null) {
changed = supertypes.add(st);
}
@@ -183,20 +188,28 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
subtypes.clear();
calculatedType = null;
}
public boolean remove(final Supertype st) {
return supertypes.remove(st);
}
public boolean remove(final String str) {
boolean changed = false;
if (CardType.isASupertype(str) && supertypes.remove(stringToSupertype.get(str))) {
changed = true;
} else if (CardType.isACardType(str) && coreTypes.remove(stringToCoreType.get(str))) {
changed = true;
} else if (subtypes.remove(str)) {
// try to remove sub type first if able
if (subtypes.remove(str)) {
changed = true;
} else {
Supertype st = Supertype.getEnum(str);
if (st != null && supertypes.remove(st)) {
changed = true;
}
CoreType ct = CoreType.getEnum(str);
if (ct != null && coreTypes.remove(ct)) {
changed = true;
}
}
if (changed) {
calculatedType = null;
}
@@ -267,15 +280,13 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
if (hasSubtype(t)) {
return true;
}
final char firstChar = t.charAt(0);
if (Character.isLowerCase(firstChar)) {
t = Character.toUpperCase(firstChar) + t.substring(1); //ensure string is proper case for enum types
}
final CoreType type = stringToCoreType.get(t);
t = StringUtils.capitalize(t);
final CoreType type = CoreType.getEnum(t);
if (type != null) {
return hasType(type);
}
final Supertype supertype = stringToSupertype.get(t);
final Supertype supertype = Supertype.getEnum(t);
if (supertype != null) {
return hasSupertype(supertype);
}
@@ -307,18 +318,18 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
return subtypes.contains(creatureType) || subtypes.contains("AllCreatureTypes");
}
private static String toMixedCase(final String s) {
if (s.equals("")) {
if (s.isEmpty()) {
return s;
}
final StringBuilder sb = new StringBuilder();
// to handle hyphenated Types
// TODO checkout WordUtils for this
final String[] types = s.split("-");
for (int i = 0; i < types.length; i++) {
if (i != 0) {
sb.append("-");
}
sb.append(types[i].substring(0, 1).toUpperCase());
sb.append(types[i].substring(1).toLowerCase());
sb.append(StringUtils.capitalize(types[i]));
}
return sb.toString();
}
@@ -606,14 +617,14 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
public static class Constant {
public static final Settable LOADED = new Settable();
public static final List<String> BASIC_TYPES = Lists.newArrayList();
public static final List<String> LAND_TYPES = Lists.newArrayList();
public static final List<String> CREATURE_TYPES = Lists.newArrayList();
public static final List<String> SPELL_TYPES = Lists.newArrayList();
public static final List<String> ENCHANTMENT_TYPES = Lists.newArrayList();
public static final List<String> ARTIFACT_TYPES = Lists.newArrayList();
public static final List<String> WALKER_TYPES = Lists.newArrayList();
public static final Set<String> BASIC_TYPES = Sets.newHashSet();
public static final Set<String> LAND_TYPES = Sets.newHashSet();
public static final Set<String> CREATURE_TYPES = Sets.newHashSet();
public static final Set<String> SPELL_TYPES = Sets.newHashSet();
public static final Set<String> ENCHANTMENT_TYPES = Sets.newHashSet();
public static final Set<String> ARTIFACT_TYPES = Sets.newHashSet();
public static final Set<String> WALKER_TYPES = Sets.newHashSet();
// singular -> plural
public static final BiMap<String,String> pluralTypes = HashBiMap.create();
// plural -> singular
@@ -662,14 +673,14 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
}
};
}
///////// Utility methods
public static boolean isACardType(final String cardType) {
return getAllCardTypes().contains(cardType);
return CoreType.isValidEnum(cardType);
}
public static ImmutableList<String> getAllCardTypes() {
public static Set<String> getAllCardTypes() {
return CoreType.allCoreTypeNames;
}
@@ -699,12 +710,12 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
return sortedSubTypes;
}
public static List<String> getBasicTypes() {
return Collections.unmodifiableList(Constant.BASIC_TYPES);
public static Collection<String> getBasicTypes() {
return Collections.unmodifiableCollection(Constant.BASIC_TYPES);
}
public static List<String> getAllCreatureTypes() {
return Collections.unmodifiableList(Constant.CREATURE_TYPES);
public static Collection<String> getAllCreatureTypes() {
return Collections.unmodifiableCollection(Constant.CREATURE_TYPES);
}
public static List<String> getAllLandTypes() {
return ImmutableList.<String>builder()
@@ -714,7 +725,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
}
public static boolean isASupertype(final String cardType) {
return (Supertype.allSuperTypeNames.contains(cardType));
return Supertype.isValidEnum(cardType);
}
public static boolean isASubType(final String cardType) {
@@ -740,7 +751,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
public static boolean isABasicLandType(final String cardType) {
return (Constant.BASIC_TYPES.contains(cardType));
}
public static boolean isAnEnchantmentType(final String cardType) {
return (Constant.ENCHANTMENT_TYPES.contains(cardType));
}

View File

@@ -28,6 +28,8 @@ public interface ICardDatabase extends Iterable<PaperCard> {
List<PaperCard> getAllCards(String cardName);
List<PaperCard> getAllCards(Predicate<PaperCard> predicate);
List<PaperCard> getAllCardsFromEdition(CardEdition edition);
Predicate<? super PaperCard> wasPrintedInSets(List<String> allowedSetCodes);
}

View File

@@ -289,13 +289,20 @@ public enum ManaCostShard {
return BinaryUtil.bitCount(this.shard & COLORS_SUPERPOSITION) == 2;
}
public boolean isGeneric() {
return isOfKind(ManaAtom.GENERIC)|| isOfKind(ManaAtom.IS_X) || this.isSnow() || this.isOr2Generic();
}
public boolean isOr2Generic() {
return isOfKind(ManaAtom.OR_2_GENERIC);
}
public boolean isColor(byte colorCode) {
return (colorCode & this.shard) > 0;
}
public boolean canBePaidWithManaOfColor(byte colorCode) {
return this.isOr2Generic() || ((COLORS_SUPERPOSITION | ManaAtom.COLORLESS) & this.shard) == 0 ||
(colorCode & this.shard) > 0;
this.isColor(colorCode);
}
public boolean isOfKind(int atom) {

View File

@@ -21,7 +21,6 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import forge.StaticData;
import forge.card.CardRules;
import forge.card.CardRulesPredicates;
@@ -37,8 +36,11 @@ import forge.util.TextUtil;
import org.apache.commons.lang3.Range;
import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
/**
* GameType is an enum to determine the type of current game. :)
@@ -71,7 +73,7 @@ public enum DeckFormat {
private final Set<String> bannedCards = ImmutableSet.of(
"Ancestral Recall", "Balance", "Black Lotus", "Black Vise", "Channel", "Chaos Orb", "Contract From Below", "Counterbalance", "Darkpact", "Demonic Attorney", "Demonic Tutor", "Earthcraft", "Edric, Spymaster of Trest", "Falling Star",
"Fastbond", "Flash", "Goblin Recruiter", "Grindstone", "Hermit Druid", "Imperial Seal", "Jeweled Bird", "Karakas", "Library of Alexandria", "Mana Crypt", "Mana Drain", "Mana Vault", "Metalworker", "Mind Twist", "Mishra's Workshop",
"Mox Emerald", "Mox Jet", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Necropotence", "Shahrazad", "Skullclamp", "Sol Ring", "Strip Mine", "Survival of the Fittest", "Sword of Body and Mind", "Time Vault", "Time Walk", "Timetwister",
"Mox Emerald", "Mox Jet", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Najeela, the Blade Blossom", "Necropotence", "Shahrazad", "Skullclamp", "Sol Ring", "Strip Mine", "Survival of the Fittest", "Sword of Body and Mind", "Time Vault", "Time Walk", "Timetwister",
"Timmerian Fiends", "Tolarian Academy", "Umezawa's Jitte", "Vampiric Tutor", "Wheel of Fortune", "Yawgmoth's Will");
@Override
@@ -324,23 +326,32 @@ public enum DeckFormat {
}
final int maxCopies = getMaxCardCopies();
if (maxCopies < Integer.MAX_VALUE) {
//Must contain no more than 4 of the same card
//shared among the main deck and sideboard, except
//basic lands, Shadowborn Apostle, Relentless Rats and Rat Colony
//Must contain no more than 4 of the same card
//shared among the main deck and sideboard, except
//basic lands, Shadowborn Apostle, Relentless Rats and Rat Colony
// Seven Dwarves can have 7 in the deck. More than 7 in deck + sb is ok in Limited
final CardPool allCards = deck.getAllCardsInASinglePool(hasCommander());
final CardPool allCards = deck.getAllCardsInASinglePool(hasCommander());
// should group all cards by name, so that different editions of same card are really counted as the same card
for (final Entry<String, Integer> cp : Aggregates.groupSumBy(allCards, PaperCard.FN_GET_NAME)) {
final IPaperCard simpleCard = StaticData.instance().getCommonCards().getCard(cp.getKey());
if (simpleCard == null) {
return TextUtil.concatWithSpace("contains the nonexisting card", cp.getKey());
}
// should group all cards by name, so that different editions of same card are really counted as the same card
for (final Entry<String, Integer> cp : Aggregates.groupSumBy(allCards, PaperCard.FN_GET_NAME)) {
final IPaperCard simpleCard = StaticData.instance().getCommonCards().getCard(cp.getKey());
// Might cause issues since it ignores "Special" Cards
if (simpleCard == null) {
return TextUtil.concatWithSpace("contains the nonexisting card", cp.getKey());
}
if (!canHaveAnyNumberOf(simpleCard) && cp.getValue() > maxCopies) {
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(maxCopies), "copies of the card", cp.getKey());
}
if (canHaveAnyNumberOf(simpleCard)) {
continue;
}
Integer cardCopies = canHaveSpecificNumberInDeck(simpleCard);
if (cardCopies != null && deck.getMain().countByName(cp.getKey(), true) > cardCopies) {
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(cardCopies), "copies of the card", cp.getKey());
}
if (cardCopies == null && cp.getValue() > maxCopies) {
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(maxCopies), "copies of the card", cp.getKey());
}
}
@@ -362,6 +373,16 @@ public enum DeckFormat {
"A deck can have any number of cards named CARDNAME.");
}
public static Integer canHaveSpecificNumberInDeck(final IPaperCard card) {
// Ideally, this would be parsed during card parsing and set this value
if (Iterables.contains(card.getRules().getMainPart().getKeywords(),
"A deck can have up to seven cards named CARDNAME.")) {
return 7;
}
return null;
}
public static String getPlaneSectionConformanceProblem(final CardPool planes) {
//Must contain at least 10 planes/phenomenons, but max 2 phenomenons. Singleton.
if (planes == null || planes.countAll() < 10) {
@@ -442,6 +463,9 @@ public enum DeckFormat {
if (this.equals(DeckFormat.Brawl)) {
return rules.canBeBrawlCommander();
}
if (this.equals(DeckFormat.TinyLeaders)) {
return rules.canBeTinyLeadersCommander();
}
return rules.canBeCommander();
}

View File

@@ -27,7 +27,7 @@ public class DeckSerializer {
}
final List<String> metadata = map.get("metadata");
if (metadata != null) {
return new DeckFileHeader(FileSection.parse(metadata, "="));
return new DeckFileHeader(FileSection.parse(metadata, FileSection.EQUALS_KV_SEPARATOR));
}
final List<String> general = map.get("general");
if (general != null) {

View File

@@ -6,6 +6,7 @@ import forge.card.CardRarity;
import forge.card.CardRules;
import forge.card.CardType.CoreType;
import forge.card.MagicColor;
import forge.util.PredicateCard;
import forge.util.PredicateString;
import org.apache.commons.lang3.StringUtils;
@@ -60,6 +61,8 @@ public interface IPaperCard extends InventoryItem {
return new PredicateNames(what);
}
public static PredicateCards cards(final List<PaperCard> what) { return new PredicateCards(what); }
private static final class PredicateColor implements Predicate<PaperCard> {
private final byte operand;
@@ -161,6 +164,25 @@ public interface IPaperCard extends InventoryItem {
}
}
private static final class PredicateCards extends PredicateCard<PaperCard> {
private final List<PaperCard> operand;
@Override
public boolean apply(final PaperCard card) {
for (final PaperCard element : this.operand) {
if (this.op(card, element)) {
return true;
}
}
return false;
}
private PredicateCards(final List<PaperCard> operand) {
super(StringOp.EQUALS);
this.operand = operand;
}
}
/**
* Pre-built predicates are stored here to allow their re-usage and
* easier access from code.

View File

@@ -108,7 +108,7 @@ public class PreconDeck implements InventoryItemFromSet {
// To be able to read "shops" section in overloads
protected PreconDeck getPreconDeckFromSections(final Map<String, List<String>> sections) {
FileSection kv = FileSection.parse(sections.get("metadata"), "=");
FileSection kv = FileSection.parse(sections.get("metadata"), FileSection.EQUALS_KV_SEPARATOR);
String imageFilename = kv.get("Image");
String description = kv.get("Description");
String deckEdition = kv.get("set");

View File

@@ -566,12 +566,8 @@ public class BoosterGenerator {
toAdd = IPaperCard.Predicates.printedInSets(sets);
} else if (operator.startsWith("fromSheet(") && invert) {
String sheetName = StringUtils.strip(operator.substring(9), "()\" ");
Iterable<PaperCard> src = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
List<String> cardNames = Lists.newArrayList();
for (PaperCard card : src) {
cardNames.add(card.getName());
}
toAdd = IPaperCard.Predicates.names(Lists.newArrayList(cardNames));
Iterable<PaperCard> cards = StaticData.instance().getPrintSheets().get(sheetName).toFlatList();
toAdd = IPaperCard.Predicates.cards(Lists.newArrayList(cards));
}
if (toAdd == null) {

View File

@@ -1,9 +1,6 @@
package forge.card;
package forge.util;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Charsets;
import forge.properties.ForgeConstants;
import forge.util.LineReader;
import java.io.FileInputStream;
import java.io.IOException;
@@ -15,12 +12,12 @@ public class CardTranslation {
private static Map <String, String> translatednames;
private static Map <String, String> translatedtypes;
private static Map <String, String> translatedoracles;
private static String languageSelected;
private static String languageSelected = "en-US";
private static void readTranslationFile(String language) {
private static void readTranslationFile(String language, String languagesDirectory) {
String filename = "cardnames-" + language + ".txt";
try (LineReader translationFile = new LineReader(new FileInputStream(ForgeConstants.LANG_DIR + filename), Charsets.UTF_8)) {
try (LineReader translationFile = new LineReader(new FileInputStream(languagesDirectory + filename), Charsets.UTF_8)) {
for (String line : translationFile.readLines()) {
String[] matches = line.split("\\|");
if (matches.length >= 2) {
@@ -34,7 +31,7 @@ public class CardTranslation {
}
}
} catch (IOException e) {
Log.error("Error reading translation file: cardnames-" + language + ".txt");
System.err.println("Error reading translation file: cardnames-" + language + ".txt");
}
}
@@ -66,7 +63,7 @@ public class CardTranslation {
}
public static HashMap<String, String> getTranslationTexts(String cardname, String altcardname) {
HashMap<String, String> translations = new HashMap<String, String>();
HashMap<String, String> translations = new HashMap<>();
translations.put("name", getTranslatedName(cardname));
translations.put("oracle", getTranslatedOracle(cardname));
translations.put("altname", getTranslatedName(altcardname));
@@ -78,14 +75,14 @@ public class CardTranslation {
return !languageSelected.equals("en-US");
}
public static void preloadTranslation(String language) {
public static void preloadTranslation(String language, String languagesDirectory) {
languageSelected = language;
if (needsTranslation()) {
translatednames = new HashMap<>();
translatedtypes = new HashMap<>();
translatedoracles = new HashMap<>();
readTranslationFile(languageSelected);
readTranslationFile(languageSelected, languagesDirectory);
}
}
}

View File

@@ -17,9 +17,12 @@
*/
package forge.util;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -56,34 +59,40 @@ public class FileSection {
protected FileSection(Map<String, String> lines0) {
lines = lines0;
}
/**
* Parses the.
*
* @param line the line
* @param kvSeparator the kv separator
* @param pairSeparator the pair separator
* @return the file section
*/
public static FileSection parse(final String line, final String kvSeparator, final String pairSeparator) {
Map<String, String> map = parseToMap(line, kvSeparator, pairSeparator);
return new FileSection(map);
}
public static Map<String, String> parseToMap(final String line, final String kvSeparator, final String pairSeparator) {
Map<String, String> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
if (!StringUtils.isEmpty(line)) {
final String[] pairs = line.split(Pattern.quote(pairSeparator));
final Pattern splitter = Pattern.compile(Pattern.quote(kvSeparator));
for (final String dd : pairs) {
final String[] v = splitter.split(dd, 2);
result.put(v[0].trim(), v.length > 1 ? v[1].trim() : "");
}
public static final Pattern DOLLAR_SIGN_KV_SEPARATOR = Pattern.compile(Pattern.quote("$"));
public static final Pattern ARROW_KV_SEPARATOR = Pattern.compile(Pattern.quote("->"));
public static final Pattern EQUALS_KV_SEPARATOR = Pattern.compile(Pattern.quote("="));
public static final Pattern COLON_KV_SEPARATOR = Pattern.compile(Pattern.quote(":"));
private static final String BAR_PAIR_SPLITTER = Pattern.quote("|");
private static Table<String, Pattern, Map<String, String>> parseToMapCache = HashBasedTable.create();
public static Map<String, String> parseToMap(final String line, final Pattern kvSeparator) {
Map<String, String> result = parseToMapCache.get(line, kvSeparator);
if (result != null) {
return result;
}
result = parseToMapImpl(line, kvSeparator);
parseToMapCache.put(line, kvSeparator, result);
return result;
}
private static Map<String, String> parseToMapImpl(final String line, final Pattern kvSeparator) {
if (StringUtils.isEmpty(line)) {
return Collections.emptyMap();
}
final Map<String, String> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
final String[] pairs = line.split(BAR_PAIR_SPLITTER);
for (final String dd : pairs) {
final String[] v = kvSeparator.split(dd, 2);
result.put(v[0].trim(), v.length > 1 ? v[1].trim() : "");
}
return Collections.unmodifiableMap(result);
}
/**
* Parses the.
*
@@ -91,11 +100,10 @@ public class FileSection {
* @param kvSeparator the kv separator
* @return the file section
*/
public static FileSection parse(final Iterable<String> lines, final String kvSeparator) {
public static FileSection parse(final Iterable<String> lines, final Pattern kvSeparator) {
final FileSection result = new FileSection();
final Pattern splitter = Pattern.compile(Pattern.quote(kvSeparator));
for (final String dd : lines) {
final String[] v = splitter.split(dd, 2);
final String[] v = kvSeparator.split(dd, 2);
result.lines.put(v[0].trim(), v.length > 1 ? v[1].trim() : "");
}

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