Compare commits

..

2861 Commits

Author SHA1 Message Date
Michael Kamensky
fbb1c906ea [maven-release-plugin] prepare release forge-1.6.43 2021-07-19 07:29:51 +03:00
Michael Kamensky
c747957cf3 - Added AFR/AFC achievements by Marek14. 2021-07-19 07:02:20 +03:00
Michael Kamensky
72b9ad8651 Merge branch 'small_fix' into 'master'
AFC edition tokens fixes

See merge request core-developers/forge!5055
2021-07-19 03:13:49 +00:00
Michael Kamensky
716503ea38 Merge branch 'master' into 'master'
AI Deck Building Bug Fixes

See merge request core-developers/forge!5060
2021-07-19 03:11:20 +00:00
Michael Kamensky
163aa7edbe Merge branch 'release-file-update' into 'master'
Update ANNOUNCEMENTS.txt

See merge request core-developers/forge!5063
2021-07-19 03:10:16 +00:00
Sol
2365e61256 Update secret_door.txt 2021-07-19 03:05:26 +00:00
Sol
7636c35fb5 Update Dungeons & Dragons Adventures in the Forgotten Realms.txt 2021-07-19 01:51:15 +00:00
Sol
7a78432976 Update ANNOUNCEMENTS.txt 2021-07-19 01:36:17 +00:00
Meerkov
6c89f2053e Fix: Multiple RemoveDeck hints now properly stack
Before: The *last* RemoveDeck hint overwrites all previous flags.

After: Every RemoveDeck hint is stored properly.
2021-07-18 15:35:52 -07:00
Meerkov
47917e4a3f Fix: AI splash best 3rd color card instead of random
AI sometimes needs to splash a 3rd color to fill their 23 playables (especially in sets with Multicolored cards). Previously, this would add cards based on the Pick order. Now it properly adds them based on the Ranking of the cards.
2021-07-18 14:28:53 -07:00
Sol
6505e5562f Merge branch 'migrate-upcoming-dnd' into 'master'
Migrate DnD

See merge request core-developers/forge!5058
2021-07-18 15:44:28 +00:00
friarsol
01a1885289 Migrate DnD 2021-07-18 11:43:47 -04:00
paul_snoops
8aee6d8e85 AFC edition tokens fixes 2021-07-18 13:32:25 +01:00
paul_snoops
789af729b2 Merge branch 'master' of https://git.cardforge.org/core-developers/forge into small_fix
 Conflicts:
	forge-gui/res/editions/Dungeons & Dragons Adventures in the Forgotten Realms Commander.txt
2021-07-18 13:29:57 +01:00
Alumi
95d459d0e6 Merge branch 'update_oracle_book' into 'master'
Update oracle text of The Book of Vile Darkness

See merge request core-developers/forge!5056
2021-07-18 12:14:57 +00:00
Lyu Zong-Hong
44f518db90 Update oracle text of The Book of Vile Darkness 2021-07-18 21:13:59 +09:00
Michael Kamensky
259a84561d Merge branch 'update_the_book_of_vile_darkness' into 'master'
Update The Book of Vile Darkness

See merge request core-developers/forge!5054
2021-07-18 11:35:55 +00:00
Michael Kamensky
0d7fb25471 Merge branch 'afc_16' into 'master'
AFC - 16 Jul

See merge request core-developers/forge!5042
2021-07-18 11:35:24 +00:00
paul_snoops
a26fef98eb AFC edition tokens fixes 2021-07-18 12:16:04 +01:00
Lyu Zong-Hong
c0fc97e81f Update The Book of Vile Darkness 2021-07-18 19:14:04 +09:00
Alumi
b29f744b45 Merge branch 'effectEmblem' into 'master'
Effect Emblem

See merge request core-developers/forge!5052
2021-07-18 09:06:49 +00:00
Hans Mackowiak
e753d45b42 Update getDefinedCards for how it get Valid* cards so the syntax is consistent with other Valid script. Also remove the hacky way to set Effect as mutable for Raging River and Plague of Vermin 2021-07-18 09:06:47 +00:00
Michael Kamensky
52160c9e60 Merge branch 'AFC_cards' into 'master'
AFC card scripts 17 Jul

See merge request core-developers/forge!5048
2021-07-18 05:33:09 +00:00
Michael Kamensky
71ff068101 Merge branch 'patch_hofri_delina' into 'master'
Update Hofri and Delina to be conformant with oracle text

See merge request core-developers/forge!5053
2021-07-18 05:32:06 +00:00
Lyu Zong-Hong
9c7704c6bd Update Hofri and Delina to be conformant with oracle text 2021-07-18 13:28:47 +09:00
Sol
f5aee89476 Update planar_ally.txt 2021-07-18 03:25:16 +00:00
paul_snoops
7180295a1f AFC card scripts 17 Jul 2021-07-17 22:37:14 +01:00
Michael Kamensky
0b3def285a Merge branch 'afc_roll_dice_part2' into 'master'
Add more AFC Roll Dice cards

See merge request core-developers/forge!5047
2021-07-17 18:27:11 +00:00
Michael Kamensky
6b3ab5edf9 Merge branch 'master' into 'master'
Added puzzle PS_MH21.

See merge request core-developers/forge!5050
2021-07-17 18:24:10 +00:00
Michael Kamensky
4dda484173 - Added puzzle PS_MH21. 2021-07-17 21:23:09 +03:00
paul_snoops
20387ffce0 AFC card scripts 17 Jul 2021-07-17 15:22:31 +01:00
Northmoc
3665ca5c2b fix typo 2021-07-17 09:48:50 -04:00
paul_snoops
eb8ac6336a AFC card scripts 17 Jul 2021-07-17 11:04:24 +01:00
Lyu Zong-Hong
d3487231f5 Add more AFC Roll Dice cards 2021-07-17 18:09:15 +09:00
Michael Kamensky
6996a172e8 Merge branch 'emergency' into 'master'
emergency_powers.txt remove unneeded

See merge request core-developers/forge!5046
2021-07-17 03:30:28 +00:00
Michael Kamensky
f05ede87fc Merge branch 'mutate_was_cast' into 'master'
Handle mutate and wasCast interaction

See merge request core-developers/forge!5044
2021-07-17 03:29:13 +00:00
Alumi
f14de1c2c1 Handle mutate and wasCast interaction 2021-07-17 03:29:13 +00:00
Michael Kamensky
0188463b7e Merge branch 'negative' into 'master'
Fix pumps from reveal

See merge request core-developers/forge!5045
2021-07-17 03:28:48 +00:00
Northmoc
6e2dd8f267 catti_brie_of_mithral_hall.txt 2021-07-16 21:48:46 -04:00
Northmoc
6d8be55032 neverwinter_hydra.txt 2021-07-16 18:32:59 -04:00
Northmoc
5e7adf6383 emergency_powers.txt remove unneeded 2021-07-16 15:48:16 -04:00
tool4EvEr
d89bff8554 Fix pumps from reveal 2021-07-16 21:15:04 +02:00
Sol
cafdc40348 Update treasure_chest.txt 2021-07-16 18:21:10 +00:00
Michael Kamensky
bc4e9ef4ac Merge branch 'AFC_Cards_20210715' into 'master'
First batch of AFC cards 15 Jul 2021

See merge request core-developers/forge!5037
2021-07-16 18:02:12 +00:00
Northmoc
d6d66e782f refactor Minn illusion token name 2021-07-16 13:57:33 -04:00
Northmoc
b5990e92db u_1_1_illusion_other_illusions.txt token for Minn 2021-07-16 13:57:10 -04:00
Northmoc
33d969db8f minn_wily_illusionist.txt 2021-07-16 13:56:34 -04:00
Northmoc
f4dcf10e41 wild_endeavor.txt 2021-07-16 10:33:27 -04:00
Northmoc
7599a24ed9 reckless_endeavor.txt 2021-07-16 10:32:54 -04:00
Wendell Wilkerson
83554b032f Merge branch 'master' into AFC_Cards_20210715 2021-07-16 09:25:21 -05:00
Wendell Wilkerson
2b12d56f17 fixes from MR threads 2021-07-16 09:24:13 -05:00
Michael Kamensky
ca65ff229f Merge branch 'Williams-master-patch-20669' into 'master'
Update Love Your LGS 2021.txt

See merge request core-developers/forge!5040
2021-07-16 13:24:53 +00:00
Michael Kamensky
38298d4b60 Merge branch 'vish' into 'master'
vish_kal_blood_arbiter.txt tidy up

See merge request core-developers/forge!5043
2021-07-16 13:24:29 +00:00
Michael Kamensky
d6c180e0da Merge branch 'fear' into 'master'
Small fix for Crippling Fear

See merge request core-developers/forge!5041
2021-07-16 13:24:15 +00:00
Northmoc
a54bb82c84 fey_steed.txt refine targeting 2021-07-16 08:38:31 -04:00
tool4EvEr
9a5fdce071 Small fix for Crippling Fear 2021-07-16 13:21:38 +02:00
Hans Mackowiak
da338f02c3 fix Xorn 2021-07-16 12:06:45 +02:00
Michael Kamensky
07a8f78c27 Merge branch 'tasha' into 'master'
Tasha's Hideous Laughter: Fix infinite loop when lib empties

See merge request core-developers/forge!5039
2021-07-16 09:51:23 +00:00
John
09774ae13f Update Love Your LGS 2021.txt 2021-07-16 09:16:33 +00:00
tool4EvEr
764a77b69e Fix infinite loop when lib empties 2021-07-16 09:44:11 +02:00
Bug Hunter
2bb3fdf088 Merge branch 'TRT-master-patch-03435' into 'master'
Update forge-gui/res/cardsfolder/i/idol_of_oblivion.txt

See merge request core-developers/forge!5038
2021-07-16 06:32:18 +00:00
Bug Hunter
de8a4c9df8 Update forge-gui/res/cardsfolder/i/idol_of_oblivion.txt 2021-07-16 06:31:51 +00:00
Wendell Wilkerson
250daad3f4 Merge branch 'master' into AFC_Cards_20210715 2021-07-16 00:05:10 -05:00
Wendell Wilkerson
d196c06c4c first batch of AFC cards 2021-07-15 23:56:49 -05:00
Michael Kamensky
d99eb6bed6 Merge branch 'holy' into 'master'
AFC: Holy Avenger and support

See merge request core-developers/forge!5036
2021-07-16 03:35:35 +00:00
Michael Kamensky
20bddf5bb4 Merge branch 'afc_roll_dice_1' into 'master'
Add AFC Roll Dice cards - part 1

See merge request core-developers/forge!5035
2021-07-16 03:35:05 +00:00
Michael Kamensky
0b664c4278 Merge branch 'afc_15' into 'master'
AFC - 15 Jul

See merge request core-developers/forge!5032
2021-07-16 03:34:25 +00:00
Michael Kamensky
0969b896c8 Merge branch 'trig' into 'master'
makeChoices for Trigger: move to stack entry

See merge request core-developers/forge!5028
2021-07-16 03:33:32 +00:00
Michael Kamensky
3d817ab16a Merge branch 'calix' into 'master'
Fix Calix

See merge request core-developers/forge!5031
2021-07-16 03:32:49 +00:00
Michael Kamensky
8a6814ce67 Merge branch 'twosat-master-patch-49093' into 'master'
Update de-DE.properties

See merge request core-developers/forge!5034
2021-07-16 03:32:15 +00:00
Michael Kamensky
61f1a2c35c Merge branch 'formats' into 'master'
Formats update

See merge request core-developers/forge!5033
2021-07-16 03:32:09 +00:00
Northmoc
01d4d6b22e support for "CanEnchantEquippedBy" restriction 2021-07-15 20:39:39 -04:00
Northmoc
e757af41f7 holy_avenger.txt 2021-07-15 20:38:47 -04:00
Northmoc
e8ccc34106 klauth_unrivaled_ancient.txt 2021-07-15 20:38:16 -04:00
Northmoc
ce7c10b37b fey_steed.txt + AI hint 2021-07-15 19:47:41 -04:00
Lyu Zong-Hong
77e29bac0b Add AFC Roll Dice cards - part 1 2021-07-16 08:45:07 +09:00
Northmoc
2ec99267da radiant_solar.txt (Marvel) 2021-07-15 19:33:30 -04:00
Northmoc
6b623f8c7f fey_steed.txt (guytrash) 2021-07-15 19:31:11 -04:00
tool4EvEr
d7badae5ea Clean up 2021-07-15 22:32:28 +02:00
Andreas Bendel
bec85590c5 Update de-DE.properties
translated lblDoYouWantRollNDiceAction
2021-07-15 19:58:11 +00:00
paul_snoops
d43c1c023e Formats update 2021-07-15 20:27:07 +01:00
paul_snoops
ecb321a584 Formats update 2021-07-15 20:23:10 +01:00
tool4EvEr
8ac9e7a383 Fix params 2021-07-15 20:43:17 +02:00
Northmoc
c327e4eec6 karazikar_the_eye_tyrant.txt 2021-07-15 14:09:19 -04:00
tool4EvEr
2cb0df9422 Fix Calix 2021-07-15 19:20:05 +02:00
Michael Kamensky
fb4d21a2cc Merge branch 'afc_15' into 'master'
AFC - 15 Jul

See merge request core-developers/forge!5027
2021-07-15 16:32:42 +00:00
Michael Kamensky
68f3c9a686 Merge branch 'fight' into 'master'
FightEffect: Fix missing trigger

See merge request core-developers/forge!5029
2021-07-15 16:26:22 +00:00
Michael Kamensky
ac78f5fa56 Merge branch 'token' into 'master'
Fix xPaid for Token as drawback

See merge request core-developers/forge!5030
2021-07-15 16:26:15 +00:00
Northmoc
e4ce75036f - add newline 2021-07-15 12:15:52 -04:00
Northmoc
ff2e67679f refactor token - rg_5_4_dragon_spirit_damagesac 2021-07-15 12:08:03 -04:00
Northmoc
60e2fce2f3 rg_5_4_dragon_spirit_damagesac.txt 2021-07-15 12:07:38 -04:00
Northmoc
48b142c7a4 vrondiss_rage_of_ancients.txt (Suthro) 2021-07-15 12:07:20 -04:00
tool4EvEr
a30438e853 Fix xPaid for Token as drawback 2021-07-15 18:03:01 +02:00
tool4EvEr
a9c92da5dd FightEffect: Fix missing trigger 2021-07-15 17:08:50 +02:00
tool4EvEr
ed3e6abe9c makeChoices for Trigger: move to stack entry 2021-07-15 17:03:07 +02:00
Northmoc
8f4a83e44a vish_kal_blood_arbiter.txt tidy up 2021-07-15 10:53:53 -04:00
Northmoc
ddae3d97a4 wild_magic_sorceror.txt better AI? 2021-07-15 09:37:12 -04:00
Northmoc
f82c7eb220 wild_magic_sorceror.txt 2021-07-15 08:41:36 -04:00
Michael Kamensky
3b9d63221c Merge branch 'tokenTable' into 'master'
Token table

See merge request core-developers/forge!4924
2021-07-15 09:14:39 +00:00
Hans Mackowiak
099970de6a Token table 2021-07-15 09:14:38 +00:00
Michael Kamensky
22e2edae1a Merge branch 'fix_ebondeath_dracolich' into 'master'
Fix Ebondeath, Dracolich can't be cast from graveyard bug

See merge request core-developers/forge!5026
2021-07-15 08:21:24 +00:00
Lyu Zong-Hong
dbdb481a2c Fix Ebondeath, Dracolich can't be cast from graveyard bug 2021-07-15 15:25:35 +09:00
Michael Kamensky
91d16eaa2d Merge branch 'update_rogue_class' into 'master'
Update Rogue Class' first ability

See merge request core-developers/forge!5025
2021-07-15 03:55:30 +00:00
Michael Kamensky
00cb0db6a2 Merge branch 'atropal' into 'master'
Refactor The Atropal

See merge request core-developers/forge!5023
2021-07-15 03:55:03 +00:00
Michael Kamensky
3d83ae70a3 Merge branch 'afc_14' into 'master'
AFC - 14 Jul

See merge request core-developers/forge!5024
2021-07-15 03:54:28 +00:00
Michael Kamensky
c0ba652956 Merge branch 'blue_skin' into 'master'
Blue variation of Magic skin

See merge request core-developers/forge!5021
2021-07-15 03:53:30 +00:00
Lyu Zong-Hong
e8646764f3 Update Rogue Class' first ability so you can still look at cards exiled even Rogue Class is not on battlefield anymore 2021-07-15 08:32:46 +09:00
Northmoc
b332fb1934 storvald_frost_giant_jarl.txt 2021-07-14 19:00:29 -04:00
Northmoc
62820e920e rename atropal - editions 2021-07-14 17:23:03 -04:00
Northmoc
b30f797964 tomb_of_annihilation.txt rename atropal 2021-07-14 17:12:18 -04:00
Northmoc
7cebead3bf rename atropal token 2021-07-14 17:12:18 -04:00
paul_snoops
5639e1639a Blue variation of Magic skin 2021-07-14 19:39:05 +01:00
Michael Kamensky
7d7c4ae3ae Merge branch 'token' into 'master'
AFR & AFC tokens update

See merge request core-developers/forge!5018
2021-07-14 17:46:00 +00:00
Michael Kamensky
2daa0ddeaa Merge branch 'fix/ray_of_frost' into 'master'
Ray of Frost as a curse for the AI

See merge request core-developers/forge!5019
2021-07-14 17:45:45 +00:00
Stu Bob
755d2d0605 Update ray_of_frost.txt 2021-07-14 17:45:18 +00:00
Michael Kamensky
1b8f73b11d Merge branch 'minim' into 'master'
minimus_containment.txt fix Affected

See merge request core-developers/forge!5017
2021-07-14 17:45:05 +00:00
stubob
a946178ad3 ray of frost as a curse for the ai 2021-07-14 11:41:38 -06:00
paul_snoops
340dee6abb AFR & AFC tokens update 2021-07-14 18:21:58 +01:00
Tim Mocny
a5852b76f0 Merge branch 'fix/Hand_of_Vecna-manditory' into 'master'
Added mandatory trigger to Hand of Vecna

See merge request core-developers/forge!5016
2021-07-14 16:25:26 +00:00
Stu Bob
f7e4ce221b removed the mandatory from the pump line 2021-07-14 16:07:32 +00:00
stubob
aaf5bd9fff Added mandatory trigger to Hand of Vecna 2021-07-14 10:05:01 -06:00
Michael Kamensky
ec122b12b9 Merge branch 'Williams-master-patch-85561' into 'master'
AFR Tokens

See merge request core-developers/forge!5015
2021-07-14 14:35:41 +00:00
Northmoc
8e645d40aa minimus_containment.txt fix Affected 2021-07-14 10:23:07 -04:00
Michael Kamensky
cae3a53825 Merge branch 'orcus2' into 'master'
orcus_prince_of_undeath.txt StackDesc tweaks & AI hint

See merge request core-developers/forge!5012
2021-07-14 12:44:12 +00:00
Tim Mocny
c0c258f03a Merge branch 'master' into 'orcus2'
# Conflicts:
#   forge-gui/res/cardsfolder/upcoming/orcus_prince_of_undeath.txt
2021-07-14 12:19:24 +00:00
John
8d8f0dbb3b Update Dungeons & Dragons Adventures in the Forgotten Realms.txt 2021-07-14 10:48:52 +00:00
Michael Kamensky
fa86c351d4 Merge branch 'roll_die_as_cost' into 'master'
Add Clay Golem and implement roll dice as cost

See merge request core-developers/forge!5014
2021-07-14 10:21:13 +00:00
Lyu Zong-Hong
cf5c96d717 Add DeckHas hint 2021-07-14 19:05:09 +09:00
Lyu Zong-Hong
5df34731b5 Add Clay Golem and implement roll dice as cost 2021-07-14 18:55:36 +09:00
Michael Kamensky
315dea3994 Merge branch 'fix_wrapped_stack_desc' into 'master'
Minor patch: A better fix for stack description of WrappedAbility

See merge request core-developers/forge!5013
2021-07-14 08:57:26 +00:00
Lyu Zong-Hong
8ec4a69cb0 Better fix for stack description of WrappedAbility 2021-07-14 15:43:50 +09:00
Michael Kamensky
2a88d598e0 Merge branch 'Adding_AFR_to_draft' into 'master'
Add draft rankings

See merge request core-developers/forge!5006
2021-07-14 03:15:18 +00:00
Michael Kamensky
efa14919aa Merge branch 'pl21' into 'master'
update PL21

See merge request core-developers/forge!5009
2021-07-14 03:14:04 +00:00
Michael Kamensky
716ac11bef Merge branch 'stackdesc_redundant_targets' into 'master'
solve redundant (Targeting: clauses in stackdescs for Wrapped triggers

See merge request core-developers/forge!5010
2021-07-14 03:12:37 +00:00
Michael Kamensky
63d7b7fda5 Merge branch 'afc_13' into 'master'
AFC - 13 Jul

See merge request core-developers/forge!5011
2021-07-14 03:11:58 +00:00
Michael Kamensky
7d94f423d4 Merge branch 'orcusTargets' into 'master'
CardUtil: fix getValidCardsToTarget for orcus

See merge request core-developers/forge!5008
2021-07-14 03:11:07 +00:00
Hans Mackowiak
1fb50f416c CardUtil: fix getValidCardsToTarget for orcus 2021-07-14 03:11:06 +00:00
Michael Kamensky
1211f41939 Merge branch 'twosat-master-patch-10087' into 'master'
Update de-DE.properties

See merge request core-developers/forge!5007
2021-07-14 03:10:33 +00:00
Northmoc
aa1f46d916 sefris + AI hints 2021-07-13 19:42:20 -04:00
Northmoc
35f2c7dacf solve redundant (Targeting: clauses in stackdescs for Wrapped triggers 2021-07-13 19:38:08 -04:00
Northmoc
d76efc5d4f orcus_prince_of_undeath.txt StackDesc tweaks & AI hint 2021-07-13 19:36:07 -04:00
stubob
3cff521da0 added booster and booster covers to the AFR edition.txt 2021-07-13 15:49:07 -06:00
stubob
e5258072d2 removed duplicate edition, put rankings at the top. 2021-07-13 15:40:38 -06:00
Andreas Bendel
9feaf90637 Update de-DE.properties
corrected little typo in lblEnableCustomCards
2021-07-13 20:16:32 +00:00
Northmoc
553b7305a2 update PL21 2021-07-13 15:19:24 -04:00
Northmoc
5f5aad4a25 sefris_of_the_hidden_ways.txt 2021-07-13 15:18:50 -04:00
stubob
21863f5c78 Added card list (didn't add dungeons though), and draft rankings (scraped from here: https://draftsim.com/AFR-pick-order.php)
I think that there is some stuff missing (currently it says that there is a template missing)
2021-07-13 13:15:07 -06:00
Michael Kamensky
4e0130f9e0 Merge branch 'AFC_edition' into 'master'
PLIST edition update

See merge request core-developers/forge!5005
2021-07-13 18:20:42 +00:00
Michael Kamensky
5f742d59b6 Merge branch 'sutured' into 'master'
sutured_ghoul.txt overhaul

Closes #1918

See merge request core-developers/forge!4997
2021-07-13 18:20:36 +00:00
Michael Kamensky
24768d42b3 Merge branch 'fix_dungeon_of_the_mad_mage' into 'master'
Fix dungeon of the mad mage and update text rendering for Dungeon cards

See merge request core-developers/forge!5002
2021-07-13 16:38:56 +00:00
paul_snoops
2bf31bfae6 PLIST edition update 2021-07-13 17:38:20 +01:00
Michael Kamensky
792d4759ca Merge branch 'trigger_planar_die' into 'master'
Roll a planar die will also run RollDie and RollDieOnce triggers

See merge request core-developers/forge!5003
2021-07-13 16:38:20 +00:00
Michael Kamensky
64271eda4c Merge branch 'AFC_edition' into 'master'
AFC edition update

See merge request core-developers/forge!5004
2021-07-13 16:38:03 +00:00
Michael Kamensky
f38c902c9c Merge branch 'afc_13' into 'master'
AFC - 13 Jul

See merge request core-developers/forge!5001
2021-07-13 16:37:46 +00:00
paul_snoops
0885fc0995 AFC edition update 2021-07-13 17:15:41 +01:00
Northmoc
14fe4b862f sutured_ghoul.txt remove Effect 2021-07-13 09:45:14 -04:00
Northmoc
d655d43910 sutured_ghoul.txt overhaul 2021-07-13 09:45:13 -04:00
Lyu Zong-Hong
de20cec586 Update Chicken à la King so only trigger when rolling d6 2021-07-13 22:07:33 +09:00
Lyu Zong-Hong
cd05a8ffe1 Trigger RollDie and RollDieOnce for planar die too 2021-07-13 21:58:52 +09:00
Lyu Zong-Hong
acc6941220 Handle <span> tag in mobile port 2021-07-13 21:19:03 +09:00
Lyu Zong-Hong
3b8b28c355 Also disable ability texts for Dungeon cards that are not current room 2021-07-13 20:49:45 +09:00
Lyu Zong-Hong
0ac9e33dc7 Fix the Runestone Caverns room in Dungeon of the Mad Mage 2021-07-13 20:07:19 +09:00
Michael Kamensky
94ae1fe777 Merge branch 'afr_12' into 'master'
AFR - 12 Jul

See merge request core-developers/forge!4996
2021-07-13 10:35:14 +00:00
Northmoc
6f3b578fc0 galea_kindler_of_hope.txt 2021-07-13 06:31:44 -04:00
Michael Kamensky
4598c67578 Merge branch 'Fix_typo' into 'master'
Fixed Typo

See merge request core-developers/forge!5000
2021-07-13 06:49:56 +00:00
stubob
1e93f31d0b Fixed Typo 2021-07-12 23:04:49 -06:00
Michael Kamensky
9de6a40028 Merge branch 'Fix_asmodeus' into 'master'
Update asmodeus_the_archfiend.txt

See merge request core-developers/forge!4998
2021-07-12 19:05:52 +00:00
Sol
1cd5b6633f Update asmodeus_the_archfiend.txt 2021-07-12 18:55:03 +00:00
Michael Kamensky
65e6a01785 Merge branch 'Williams-master-patch-28964' into 'master'
AFR 12/07

See merge request core-developers/forge!4994
2021-07-12 16:13:50 +00:00
Michael Kamensky
f5561a17f9 Merge branch 'AFR_fixes' into 'master'
Fixes from Alumi comments

See merge request core-developers/forge!4995
2021-07-12 16:12:47 +00:00
ArsenalNut
a3d5094175 Update skullport_merchant.txt 2021-07-12 15:52:29 +00:00
Northmoc
82a9582f34 inferno_of_the_star_mounts.txt 2021-07-12 10:45:52 -04:00
Wendell Wilkerson
7846128ba5 fixes from Alumi comments 2021-07-12 09:28:13 -05:00
Northmoc
4b542b9002 sudden_insight.txt 2021-07-12 10:23:07 -04:00
Northmoc
da4e428cb8 purple_worm.txt 2021-07-12 08:26:48 -04:00
John
0c6d965a1c Update circle_of_the_moon_druid.txt 2021-07-12 12:04:51 +00:00
John
cfa2432cca Update rally_maneuver.txt 2021-07-12 11:59:38 +00:00
John
0c61ae7a2b Update lurking_roper.txt 2021-07-12 11:59:14 +00:00
John
9b8654a1af Update elturgard_ranger.txt 2021-07-12 11:58:51 +00:00
John
ecf3dcca99 Update dire_wolf_prowler.txt 2021-07-12 11:58:22 +00:00
John
02c76474c8 Add new file 2021-07-12 10:27:11 +00:00
John
f8832a19c0 Add new file 2021-07-12 10:26:41 +00:00
John
413352a4d5 Add new file 2021-07-12 10:26:05 +00:00
John
fcd7fe840d Add new file 2021-07-12 10:25:38 +00:00
John
2843bc18fc Add new file 2021-07-12 10:25:09 +00:00
John
478b8e604f Add new file 2021-07-12 10:24:42 +00:00
John
b9554604b6 Add new file 2021-07-12 10:24:13 +00:00
John
aec56d726d Add new file 2021-07-12 10:23:48 +00:00
John
863d1d4884 Add new file 2021-07-12 10:23:12 +00:00
John
f2cfeb31b8 Add new file 2021-07-12 10:22:43 +00:00
John
ea50d8f947 Add new file 2021-07-12 10:22:11 +00:00
John
ee835d310a Add new file 2021-07-12 10:21:39 +00:00
John
4567260d61 Add new file 2021-07-12 10:21:05 +00:00
John
d0a4ff54f7 Add new file 2021-07-12 10:19:18 +00:00
Alumi
767b6f1ea5 Merge branch 'patch-enable-custom-cards' into 'master'
Patch showing custom card warning properly

See merge request core-developers/forge!4993
2021-07-12 08:31:11 +00:00
leriomaggio
4eb2bcca89 Merge remote-tracking branch 'upstream/master' into patch-enable-custom-cards 2021-07-12 09:26:16 +01:00
leriomaggio
6b60e478ee Patch showing custom card warning properly 2021-07-12 09:22:15 +01:00
Michael Kamensky
ecd7afdf4a Merge branch 'treasuremana' into 'master'
AFR: mana from Treasure cards

See merge request core-developers/forge!4970
2021-07-12 07:40:03 +00:00
Michael Kamensky
31beff4f4f Merge branch 'AFR_Artifacts' into 'master'
add last AFR artifacts and updated counter enum list to support HARMONY counters

See merge request core-developers/forge!4991
2021-07-12 07:37:32 +00:00
Michael Kamensky
642915a798 Merge branch 'AFR_Cards_20210710' into 'master'
AFR cards 20210710

See merge request core-developers/forge!4986
2021-07-12 07:37:15 +00:00
Michael Kamensky
6cdc5c0cf8 Merge branch 'AFR_another_card' into 'master'
Add Split the Party

See merge request core-developers/forge!4992
2021-07-12 07:36:26 +00:00
Michael Kamensky
eb20ff0a2a Merge branch 'add_dragons_fire' into 'master'
Add Dragon's Fire

See merge request core-developers/forge!4988
2021-07-12 07:36:06 +00:00
Wendell Wilkerson
657eda2062 added Split the Party 2021-07-12 01:48:23 -05:00
Wendell Wilkerson
a6bf79500d fixes from comments 2021-07-12 00:32:18 -05:00
Wendell Wilkerson
d4ff181a1f added AILogic$ Aristocrat and fixed oracle text 2021-07-12 00:19:46 -05:00
Michael Kamensky
e516b00fa9 Merge branch 'afr_11' into 'master'
AFR - 11 Jul

See merge request core-developers/forge!4989
2021-07-12 04:50:28 +00:00
Michael Kamensky
6138235b8b Merge branch 'add-custom-cards-support' into 'master'
New Option in Forge to enable the use of Custom Cards

Closes #1915

See merge request core-developers/forge!4987
2021-07-12 04:48:56 +00:00
Wendell Wilkerson
fd54ecb571 add Trickster's Talisman and fixed stack descriptions 2021-07-11 23:25:59 -05:00
Wendell Wilkerson
eec8b7a42a add AFR artifact and updated counter enum list to support HARMONY counters 2021-07-11 22:31:58 -05:00
Northmoc
ccfe318363 dragons_disciple.txt 2021-07-11 22:37:03 -04:00
Lyu Zong-Hong
a83c06baf0 Add Dragon's Fire 2021-07-12 11:27:22 +09:00
Wendell Wilkerson
c7eff32f94 fixes from thread comments 2021-07-11 16:54:51 -05:00
leriomaggio
e67402d5ae Change of policy for custom cards load
I've decided to use the option in settings only for gameplay, and so always allowing the load of custom cards.

This is because otherwise the option should be much more pervasive, so inhibiting card catalog, sets filters, ...
So the option will just control the use of custom cards in decks for gameplay (still false by default!)
2021-07-11 19:48:23 +01:00
leriomaggio
81e35ca7f7 Updated and Added translations for all langs for the new options 2021-07-11 19:45:47 +01:00
leriomaggio
72f3411242 CustomCard option also added to mobile settings pane. 2021-07-11 19:15:48 +01:00
leriomaggio
75f04de148 Merge remote-tracking branch 'upstream/master' into add-custom-cards-support 2021-07-11 19:10:52 +01:00
leriomaggio
2189a33171 A new "Custom Cards Deck" format will be returned for decks containing custom cards and not being matched with any other formats. 2021-07-11 19:10:32 +01:00
leriomaggio
72e40504d7 Deck conformance check now includes custom cards to be (eventually) used in games. 2021-07-11 19:07:23 +01:00
leriomaggio
811d6479f5 CardPool now includes the option to load custom cards and check preferences.
If Custom Card setting is disabled, an error message in logs will be printed accordingly.
Custom Cards will be loaded as such in Card Pool only if the option in settings is enabled.
2021-07-11 19:06:39 +01:00
leriomaggio
e6958305b5 StaticData now includes an extra option to control the use of Custom Cards
This option is read from ForgePreferences and passed to StaticData by FModel
2021-07-11 19:04:56 +01:00
leriomaggio
2808b4945e New Option in Forge Preference to enable the use of custom cards
A new Option has been added in Forge Preferences to enable the use of custom cards.
This option (default is False for backward compatibility) will control whether custom cards can be read in custom editions, and so decks containing them can be used in game matches.

Also, en-US translation file received an update to clarify what is intended with Unknonw cards (to be distinguished from custom cards now)
2021-07-11 19:03:50 +01:00
Wendell Wilkerson
99dcd03e64 optimized scripts for Burning Hands and Divine Smite 2021-07-11 12:17:47 -05:00
Wendell Wilkerson
5cf3458a5e removed backup file 2021-07-11 11:50:54 -05:00
Wendell Wilkerson
55e2cdddb7 Merge branch 'master' into AFR_Cards_20210710 2021-07-11 11:46:14 -05:00
Wendell Wilkerson
b130e7f658 third batch 2021-07-11 11:43:43 -05:00
Bug Hunter
b6dfbd5e2c Merge branch 'arsenal' into 'master'
Fix Arsenal Thresher

See merge request core-developers/forge!4985
2021-07-11 15:37:16 +00:00
tool4EvEr
79bb2639a8 Fix Arsenal Thresher 2021-07-11 17:36:49 +02:00
Wendell Wilkerson
ba2a48600c second batch 2021-07-11 10:35:31 -05:00
Hans Mackowiak
cc8c58c961 Merge branch 'gristFixes' into 'master'
Card: fix getType when using changedCardTypesCharacterDefining

See merge request core-developers/forge!4984
2021-07-11 15:15:55 +00:00
Hans Mackowiak
73562528f6 Card: fix getType when using changedCardTypesCharacterDefining 2021-07-11 15:15:54 +00:00
Michael Kamensky
cbd8f0297c Merge branch 'afc_roll_dice' into 'master'
Update RollDice effect to support AFC cards

See merge request core-developers/forge!4983
2021-07-11 14:42:10 +00:00
Michael Kamensky
f48fa6baef Merge branch 'gain_class_level' into 'master'
Implement Class mechanism

See merge request core-developers/forge!4955
2021-07-11 13:17:53 +00:00
Alumi
83eb0f8ee4 Implement Class mechanism 2021-07-11 13:17:51 +00:00
Lyu Zong-Hong
4c8acb251e Update RollDice effect to support AFC cards 2021-07-11 19:13:47 +09:00
Michael Kamensky
39a0b9b543 Merge branch 'fixing' into 'master'
Small fixes

Closes #1917

See merge request core-developers/forge!4982
2021-07-11 08:51:24 +00:00
tool4EvEr
4b785fd75b Fix Scavenge for CDA 2021-07-11 09:45:14 +02:00
tool4EvEr
c491f6dfb6 Fix FailedToTarget 2021-07-11 09:44:57 +02:00
tool4EvEr
2e55b6c884 Fix zone 2021-07-11 09:44:39 +02:00
Wendell Wilkerson
25d30254a1 first batch of AFR cards 2021-07-11 01:25:11 -05:00
Michael Kamensky
028307953b Merge branch 'quick-update-sets-filter-per-deck-manager' into 'master'
Update and FIX to Set Filters for Card and Deck Catalogs

See merge request core-developers/forge!4979
2021-07-11 04:02:52 +00:00
Michael Kamensky
d4b0c648e9 Merge branch 'afr_jul_10' into 'master'
Added AFR RollDice cards and Temple of the Dragon Queen

See merge request core-developers/forge!4978
2021-07-11 04:01:53 +00:00
Alumi
4fe67ccb8c Added AFR RollDice cards and Temple of the Dragon Queen 2021-07-11 04:01:53 +00:00
Michael Kamensky
6ff7ccd3ac Merge branch 'equipExtras' into 'master'
CardFactoryUtil: add way to add Extra Parameters and Extra Description

See merge request core-developers/forge!4980
2021-07-11 04:01:28 +00:00
Michael Kamensky
800a43eba4 Merge branch 'surveilFixChangesAll' into 'master'
SurveilEffect: fix triggerChangesZoneAll

See merge request core-developers/forge!4981
2021-07-11 04:00:54 +00:00
Michael Kamensky
a2ed1c386f Merge branch 'AFR_charm_cards' into 'master'
AFR charm cards

See merge request core-developers/forge!4975
2021-07-11 04:00:36 +00:00
Hans Mackowiak
a8cba0eef4 SurveilEffect: fix triggerChangesZoneAll 2021-07-11 03:28:05 +02:00
Hans Mackowiak
e0f01a3168 CardFactoryUtil: add way to add Extra Parameters and Extra Description 2021-07-11 03:01:21 +02:00
leriomaggio
a132e9a3d4 Merge remote-tracking branch 'upstream/master' into quick-update-sets-filter-per-deck-manager 2021-07-10 21:24:48 +01:00
leriomaggio
d751236154 CardSet and DeckSet filters now include the extra option to instantiate DialogChooseSets with list of allowed sets.
This also fixes a BUG for DeckSetFilter that did not update properly the list of filters in ItemManager.
This had the effect of not updating the labels for set filters for deck, after edit.
2021-07-10 21:24:21 +01:00
leriomaggio
32e3d00cec ItemManager now includes a new special method to get filtered set codes from CardPool
The method is implemented in the abstract super class (ItemManager) and so inherited by `CardManager` (where there is no format nor game type that could be used), and specialised by `DeckManager`.

In particular, the only current format with limitations on sets is Brawl, and so the only one accounted for.
In any case, the list of (unique) allowed sets is automatically inferred by default from the cardpool (as in the case of `CardManager`).
It is important to note that this filter is stored into an attribute, and so the list is calculated only once.

The results of this list will be passed on the `DialogChooseSets` to update the content of the dialog panel, accordingly.
2021-07-10 21:22:40 +01:00
leriomaggio
b800e478f1 New constructor with limitedSets list for dual of unselectable
There are cases in which the set filter should be initialised with the white list of allowed set codes, rather than the blacklist (i.e. unselectableSets).

This is the case of CardCatalog or DeckCatalog with constraints or limitations on card pool (i.e. restricted cards, and or restricted sets).

This new set of constructors with extra parameters allow to handle this cases, so that the UI can easily adapt.
2021-07-10 21:19:05 +01:00
leriomaggio
f655151a6e FIXED a bug that did not consider initialised checked leaf nodes 2021-07-10 21:15:35 +01:00
Wendell Wilkerson
5d6939b2f2 Merge branch 'master' into AFR_charm_cards 2021-07-10 10:37:25 -05:00
Wendell Wilkerson
30d276aa29 fixes to address thread comments 2021-07-10 10:35:17 -05:00
Michael Kamensky
3dd44ba71a Merge branch 'update_wrapped_description' into 'master'
Update the implementation of trigger replacing ABILITY text

Closes #1914

See merge request core-developers/forge!4976
2021-07-10 12:33:59 +00:00
Lyu Zong-Hong
33f5080ca6 Fix WrappedAbility again so simultaneous trigger text is shown 2021-07-10 18:43:27 +09:00
Michael Kamensky
affbd917b3 Merge branch 'path' into 'master'
Fix Descendants' Path shuffling

See merge request core-developers/forge!4977
2021-07-10 09:05:51 +00:00
Hans Mackowiak
3126d7ac5d Merge branch 'treasureSpent' into 'master'
CastTotalManaSpent Treasure

See merge request core-developers/forge!4948
2021-07-10 07:49:13 +00:00
Hans Mackowiak
1f0e49d1d4 CastTotalManaSpent Treasure 2021-07-10 07:49:12 +00:00
tool4EvEr
60d4991a84 Fix Descendants' Path shuffling 2021-07-10 09:41:35 +02:00
Lyu Zong-Hong
d5149293da Update the implementation of trigger replacing ABILITY text. 2021-07-10 15:22:55 +09:00
Wendell Wilkerson
75ccabff9c added creatures with charm triggers 2021-07-09 23:30:37 -05:00
Michael Kamensky
dd6dc739fb Merge branch 'propertyfix' into 'master'
ThisTargetedController

Closes #1909

See merge request core-developers/forge!4974
2021-07-10 03:42:11 +00:00
Wendell Wilkerson
ac094f4af4 added AFR charm instants 2021-07-09 22:11:07 -05:00
tool4EvEr
cdb2330792 Fix Ram Through 2021-07-09 20:22:23 +02:00
Michael Kamensky
7744552450 Merge branch 'stackdesc' into 'master'
Various fixes

See merge request core-developers/forge!4972
2021-07-09 16:15:59 +00:00
Michael Kamensky
6de4789428 Merge branch 'afr_9' into 'master'
AFR - 9 Jul

See merge request core-developers/forge!4973
2021-07-09 16:15:48 +00:00
Northmoc
3af8a7dfa4 add "RememberRoll" to RollDiceEffect 2021-07-09 11:10:50 -04:00
Northmoc
9e92e46cad goblin_morningstar.txt 2021-07-09 11:10:28 -04:00
Northmoc
962d5c2105 aberrant_mind_sorceror.txt 2021-07-09 10:17:46 -04:00
Northmoc
ef4536d033 dragon_turtle.txt 2021-07-09 08:55:19 -04:00
Northmoc
01376d82db said_done.txt fix Duration 2021-07-09 08:49:27 -04:00
Northmoc
570abfcd21 spoils_of_the_hunt.txt - add AILogic 2021-07-09 08:08:13 -04:00
Northmoc
5bf0ad547e hunters_edge.txt - add AILogic 2021-07-09 08:07:32 -04:00
Northmoc
f8a256ea27 hunters_edge.txt tidy up 2021-07-09 08:00:23 -04:00
Northmoc
e46001cc91 price_of_loyalty.txt fix Pump param 2021-07-09 07:56:34 -04:00
Northmoc
07261e8525 spoils_of_the_hunt.txt 2021-07-09 07:55:00 -04:00
Northmoc
64f022f809 kalain_reclusive_painter.txt 2021-07-09 07:22:55 -04:00
Northmoc
c07a63cd7e price_of_loyalty.txt 2021-07-09 07:22:54 -04:00
Northmoc
60b27803ce jaded_sell_sword.txt 2021-07-09 07:22:54 -04:00
Northmoc
c5c58885e6 devour_intellect.txt 2021-07-09 07:22:53 -04:00
Michael Kamensky
0c748c091e Merge branch 'dividedfix' into 'master'
Fix assignGenericAmount dialog when no targets (Lathiel)

See merge request core-developers/forge!4967
2021-07-09 09:42:37 +00:00
Michael Kamensky
8c8c8ba0c7 Merge branch 'twosat-master-patch-51857' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4971
2021-07-09 09:31:44 +00:00
Andreas Bendel
2e03b172b2 Update de-DE.properties
'translated' Ante and Sideboard label
not much to do
2021-07-09 06:21:10 +00:00
Michael Kamensky
6474f2a7eb Merge branch 'small_fixes' into 'master'
AFR card scripts 08 Jul

See merge request core-developers/forge!4968
2021-07-09 04:14:33 +00:00
Michael Kamensky
51df73d918 Merge branch 'afr_8' into 'master'
AFR - 8 Jul

See merge request core-developers/forge!4966
2021-07-09 04:13:36 +00:00
Michael Kamensky
98cf75e92e Merge branch 'card_targeting_fixes' into 'master'
Card script fixes for Issue #1893

See merge request core-developers/forge!4963
2021-07-09 04:12:42 +00:00
Michael Kamensky
43ac1fabcf Merge branch 'akoum' into 'master'
PumpAll: better trigger decision

See merge request core-developers/forge!4969
2021-07-09 04:11:16 +00:00
Wendell Wilkerson
f06fa47e75 Merge branch 'master' into card_targeting_fixes 2021-07-08 21:56:35 -05:00
Wendell Wilkerson
06c60a2540 updated from MR comments 2021-07-08 21:48:04 -05:00
Northmoc
8bf557cc19 check_for_traps.txt fix reveal, descs 2021-07-08 20:38:50 -04:00
paul_snoops
00e256edf7 AFR card scripts 08 Jul 2021-07-08 22:16:56 +01:00
paul_snoops
cd7bd9263a AFR card scripts 08 Jul 2021-07-08 22:15:54 +01:00
paul_snoops
933d92bdd4 AFR card scripts 08 Jul 2021-07-08 22:04:58 +01:00
tool4EvEr
101ce59f06 Clean up 2021-07-08 21:04:32 +02:00
tool4EvEr
3e16390999 PumpAll: better trigger decision 2021-07-08 21:04:17 +02:00
paul_snoops
4c71f76260 AFR card scripts 08 Jul 2021-07-08 18:46:15 +01:00
Northmoc
c7cb42cd7b skeletal_swarming.txt 2021-07-08 12:16:00 -04:00
tool4EvEr
a1964eb75e Fix assignGenericAmount dialog when no targets (Lathiel) 2021-07-08 17:49:42 +02:00
Northmoc
fb0c44fce6 long_rest.txt 2021-07-08 11:33:13 -04:00
Northmoc
3f263fefee ochre_jelly.txt 2021-07-08 11:33:12 -04:00
Michael Kamensky
d7b252185a Merge branch 'afr_7' into 'master'
AFR: 7 Jul

See merge request core-developers/forge!4965
2021-07-08 04:21:09 +00:00
Northmoc
2cc6128f1f treasure_chest.txt 2021-07-07 23:46:01 -04:00
Northmoc
da6c027315 treasure_vault.txt 2021-07-07 23:45:24 -04:00
Northmoc
e4f7bd1f68 hobgoblin_bandit_lord.txt 2021-07-07 23:17:25 -04:00
Northmoc
5de6401ed8 xorn.txt 2021-07-07 22:58:10 -04:00
Northmoc
2e469be484 flameskull.txt 2021-07-07 22:58:10 -04:00
Northmoc
2074320961 westgate_regent.txt 2021-07-07 22:58:09 -04:00
Northmoc
626124c912 true_polymorph.txt 2021-07-07 22:58:09 -04:00
Wendell Wilkerson
2bf066b937 Merge branch 'master' into card_targeting_fixes 2021-07-07 20:47:29 -05:00
Michael Kamensky
8de27ddb0c Merge branch 'afr_7' into 'master'
AFR - 7 Jul

See merge request core-developers/forge!4964
2021-07-07 20:26:40 +00:00
Michael Kamensky
030ed46cae Merge branch 'AFR_7_7' into 'master'
AFR card scripts 07 Jul

See merge request core-developers/forge!4962
2021-07-07 20:26:09 +00:00
Michael Kamensky
b85b067e42 Merge branch 'bruenor' into 'master'
AFR: Bruenor Battlehammer

See merge request core-developers/forge!4953
2021-07-07 20:25:25 +00:00
Northmoc
74b439adaf AbilityUtils.java support "DungeonCompletedNamed" 2021-07-07 16:10:59 -04:00
Northmoc
d020e8a7d1 acererak_the_archlich.txt 2021-07-07 16:06:37 -04:00
Northmoc
27624ae6e2 blue_dragon.txt fix 2021-07-07 15:13:08 -04:00
Northmoc
10bbd4d07f blue_dragon.txt 2021-07-07 15:05:21 -04:00
Northmoc
a2fa475c43 white_dragon.txt 2021-07-07 14:46:20 -04:00
Northmoc
9965517329 loyal_warhound.txt 2021-07-07 14:46:04 -04:00
paul_snoops
9469f1bdaf AFR card scripts 07 Jul 2021-07-07 19:19:26 +01:00
paul_snoops
5163836668 AFR card scripts 07 Jul 2021-07-07 16:57:34 +01:00
paul_snoops
322008b458 AFR card scripts 07 Jul 2021-07-07 16:34:49 +01:00
paul_snoops
059641f71d AFR card scripts 07 Jul 2021-07-07 15:45:51 +01:00
Northmoc
b383262e70 GameActionUtil.java - add Equip 0 code block similar to Cycling 0 2021-07-07 10:04:45 -04:00
Wendell Wilkerson
636cbaeb5f fixed charm cards in bug reports 2021-07-07 08:44:05 -05:00
Northmoc
866602a683 MagicStack.java log number of Equips this turn 2021-07-07 09:43:11 -04:00
Northmoc
0638670b7c remove Equip Trigger stuff from Player.java 2021-07-07 09:43:10 -04:00
Northmoc
9df082f448 AbilityUtils.java YouEquippedThisTurn XMath 2021-07-07 09:43:10 -04:00
Northmoc
66e8cc5bff Player.java support equippedThisTurn 2021-07-07 09:43:10 -04:00
Northmoc
7188f2ccb3 bruenor_battlehammer.txt 2021-07-07 09:43:09 -04:00
paul_snoops
28cd8c877d AFR card scripts 07 Jul 2021-07-07 14:03:54 +01:00
paul_snoops
ccd2f2c1c6 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2021-07-07 12:45:56 +01:00
Michael Kamensky
bda997ef1e Merge branch 'wish' into 'master'
Add Wish and update Frontier Explorer

See merge request core-developers/forge!4960
2021-07-07 10:36:39 +00:00
Lyu Zong-Hong
6849578899 Add Wish and update Frontier Explorer 2021-07-07 18:13:06 +09:00
paul_snoops
96785e1353 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2021-07-07 05:11:52 +01:00
Michael Kamensky
a02f29ba8a Merge branch 'AFR_cards' into 'master'
AFR card scripts 05 Jul

See merge request core-developers/forge!4950
2021-07-07 02:55:46 +00:00
Michael Kamensky
76e361287a Merge branch 'vecna' into 'master'
AFR: Vecna

See merge request core-developers/forge!4959
2021-07-07 02:55:03 +00:00
Michael Kamensky
2f9e2dc2ef Merge branch 'afr_6' into 'master'
AFR - 6 July

See merge request core-developers/forge!4957
2021-07-07 02:53:23 +00:00
Wendell Wilkerson
a3b685a937 fixed two target DealDamage cards 2021-07-06 21:29:00 -05:00
Northmoc
9543534dda AnimateEffectBase - support "GainsTriggeredAbilitiesOf" 2021-07-06 19:58:34 -04:00
Northmoc
af0056d971 the_book_of_vile_darkness.txt 2021-07-06 19:57:30 -04:00
Northmoc
cec46460f4 hand_of_vecna.txt 2021-07-06 19:57:14 -04:00
Northmoc
789fb973a9 vecna token 2021-07-06 19:56:43 -04:00
paul_snoops
1a38a62941 AFR card scripts 05 Jul 2021-07-06 22:52:04 +01:00
paul_snoops
a52df6e4d1 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2021-07-06 18:21:22 +01:00
paul_snoops
82ce6d1e3d AFR card scripts 05 Jul 2021-07-06 18:20:38 +01:00
paul_snoops
f71804a56f AFR card scripts 05 Jul 2021-07-06 18:13:33 +01:00
Bug Hunter
5f0662c139 Merge branch 'import' into 'master'
Fix import

See merge request core-developers/forge!4958
2021-07-06 16:16:02 +00:00
tool4EvEr
c70b3ba4ae Fix import 2021-07-06 18:15:49 +02:00
Bug Hunter
4fbe9084e2 Merge branch 'twosat-master-patch-21869' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4956
2021-07-06 16:08:06 +00:00
Bug Hunter
2381589bea Merge branch 'add_sideboard_ante_label' into 'master'
Add Ante and Sideboard label to PlayerDetailsPanel to allow view cards in them

See merge request core-developers/forge!4940
2021-07-06 16:07:32 +00:00
Alumi
fcdb9b25bd Add Ante and Sideboard label to PlayerDetailsPanel to allow view cards in them 2021-07-06 16:07:31 +00:00
Northmoc
6cc745526b teleportation_circle.txt 2021-07-06 10:19:06 -04:00
Northmoc
745a33ef8c mind_flayer.txt 2021-07-06 10:18:50 -04:00
Andreas Bendel
dc54a94bcc Update de-DE.properties
lblBlock was missing
2021-07-06 12:06:36 +00:00
paul_snoops
eb519f4278 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2021-07-06 11:08:27 +01:00
paul_snoops
934c780b73 AFR card scripts 05 Jul 2021-07-06 11:05:28 +01:00
paul_snoops
dc0ed6e233 AFR card scripts 05 Jul 2021-07-06 11:02:29 +01:00
Wendell Wilkerson
9245841d4d fixed Acorn Catapult not creating token when planeswalker targeted, fixed discard and token issues with Prismari Command 2021-07-06 03:44:47 -05:00
Michael Kamensky
49a85dbe55 Merge branch 'master' into 'master'
update simplified chinese translations

See merge request core-developers/forge!4954
2021-07-06 05:43:40 +00:00
CCTV-1
e2b7fe37aa change traditional chinese to simplified chinese. 2021-07-06 13:18:38 +08:00
Michael Kamensky
251ffbf574 Merge branch 'editions-type-review' into 'master'
ReNEWED Edition Types and Dates for Card Edition, ReNewed `Set` filter dialog panel in Desktop, NEW `Blocks` Card|Deck Catalog Filter, Updates to Formats

See merge request core-developers/forge!4878
2021-07-06 04:23:24 +00:00
paul_snoops
0d01aeaceb AFR card scripts 05 Jul 2021-07-06 00:20:55 +01:00
paul_snoops
9fa1b6e585 Revert "AFR card scripts 05 Jul"
This reverts commit d3dded47
2021-07-06 00:11:09 +01:00
paul_snoops
d3dded4759 AFR card scripts 05 Jul 2021-07-06 00:01:10 +01:00
Bug Hunter
ae32a9f27b Merge branch 'playfix' into 'master'
Small PlayEffect fix

See merge request core-developers/forge!4952
2021-07-05 21:55:04 +00:00
tool4EvEr
80bc6785ba Small PlayEffect fix 2021-07-05 23:54:27 +02:00
paul_snoops
622fbe3192 AFR card scripts 05 Jul 2021-07-05 22:18:16 +01:00
Michael Kamensky
7516882c0d Merge branch '1911-painter-s-servant-devoid' into 'master'
Resolve "Painter's Servant + Devoid"

Closes #1911

See merge request core-developers/forge!4951
2021-07-05 18:57:21 +00:00
Hans Mackowiak
7a1a657ae9 Card: changedCardTypesCharacterDefining + changedCardColorsCharacterDefining for better apply in Order 2021-07-05 20:03:07 +02:00
paul_snoops
0f4af36c17 AFR card scripts 05 Jul 2021-07-05 16:15:50 +01:00
paul_snoops
70677a963a AFR card scripts 05 Jul 2021-07-05 16:10:32 +01:00
Michael Kamensky
41c3d6ebed Merge branch 'kethis' into 'master'
Small fixes

See merge request core-developers/forge!4945
2021-07-05 14:50:59 +00:00
Michael Kamensky
bdff72745a Merge branch 'volo_guide_to_monsters' into 'master'
Add Volo, Guide to Monsters and support invert condition for card properties

See merge request core-developers/forge!4949
2021-07-05 14:48:27 +00:00
paul_snoops
b57e3e89bf AFR card scripts 05 Jul 2021-07-05 14:12:47 +01:00
paul_snoops
974b027cf6 AFR card scripts 05 Jul 2021-07-05 13:30:11 +01:00
paul_snoops
7d884eb61e AFR card scripts 05 Jul 2021-07-05 13:00:39 +01:00
paul_snoops
883d7ce476 AFR card scripts 05 Jul 2021-07-05 12:58:50 +01:00
leriomaggio
921fc43f75 Updates to Legacy and Vintage with left out sets 2021-07-05 11:18:47 +01:00
leriomaggio
42223f109e Merge remote-tracking branch 'upstream/master' into editions-type-review 2021-07-05 09:40:34 +01:00
leriomaggio
4c264b7e3d Changed labels for format restrctions to Sanctioned formats (en only)
Other languages would be OK the same with previous wordings.
2021-07-05 09:38:47 +01:00
leriomaggio
dab893c24f Changed options not showing node counts on disabled nodes. 2021-07-05 09:35:02 +01:00
leriomaggio
a276118409 Set restrictions to Legacy and Vintage to allow for proper edition selection in sets filters 2021-07-05 09:22:25 +01:00
leriomaggio
57c81ec49f Updated to new DRAFT edition type 2021-07-05 08:14:46 +01:00
leriomaggio
411e77c8c6 Modern format now also includes: TSR, MMA, MM3, W16 sets
Modern format now inclides TimeSpiral Remastered, Modern Masters (all) and
Welcome Deck 2016 (all new frame cards, since also W17 was included)
2021-07-05 08:06:46 +01:00
leriomaggio
af12354366 G18 Set added to Pioneer format 2021-07-05 08:05:02 +01:00
leriomaggio
81ec996ab3 DRAFT_INNOVATION card edition type renamed as DRAFT
The new name IMHO is easier and more appropriate as a category name.
This would be a mismatch with Scryfall Nomenclature, but Forge contains already mismatch (e.g. REPRINT) so not a big deal.
2021-07-05 08:04:19 +01:00
Alumi
756132c8af Merge branch 'GristFixes' into 'master'
Grist: Better check for StaticAbility using ExcludeZone, fix timestamp on...

See merge request core-developers/forge!4947
2021-07-05 05:53:15 +00:00
Lyu Zong-Hong
f7694970c8 Add Volo, Guide to Monsters and support negate condition for card properties 2021-07-05 13:26:09 +09:00
Michael Kamensky
8ca4432482 Merge branch 'AFR_cards' into 'master'
AFR card scripts 04 Jul

See merge request core-developers/forge!4946
2021-07-05 02:41:40 +00:00
Michael Kamensky
b51436d4f1 Merge branch 'roll_a_d20' into 'master'
Update RollDiceEffect to better support d20 mechanism in AFR

See merge request core-developers/forge!4944
2021-07-05 02:38:58 +00:00
Alumi
dc793aa266 Update RollDiceEffect to better support d20 mechanism in AFR 2021-07-05 02:38:57 +00:00
leriomaggio
cabfe0a33a Merge remote-tracking branch 'upstream/master' into editions-type-review 2021-07-05 01:21:39 +01:00
paul_snoops
d22d3f21da AFR card scripts 04 Jul 2021-07-05 01:20:36 +01:00
leriomaggio
6e2d5c13b4 NDeckManager now includes the new DeckBlockFilter if preferences in menu
say to load HIstoric formats
2021-07-05 01:18:24 +01:00
leriomaggio
1252b25b0a NEW DeckBlockFilter for Deck Catalog.
This new filter mirrors (and extends) the one on Sets, which also relies on the renewed buildPredicate implementatio which looks at the edition of
a DeckProxy.
2021-07-05 01:15:00 +01:00
leriomaggio
4372311297 buildPredicate specialised for DeckSet filters
Now this new implementation brings the deck set filter to finally work as expected.
In particular, instead of checking that all cards in Deck Pools belong to a unique edition, the predicate now leverages on the getEdition() method of DeckProxy instances, which returns the latest of the earliest
sets of cards. This function will be subject to change into a future Merge Request state.
2021-07-05 01:11:59 +01:00
leriomaggio
5b67c08ae7 buildPredicate not final anymore to allow for override in subclasses
This is the first step to finally make the DeckSetFilter working properly!
So far, the filter applies if **all** the cards in a deck matches selected specific set(s).
We will allow for customisation to simplify and correct this filter - more in the next commit.
2021-07-05 01:06:25 +01:00
paul_snoops
f0c1062c21 AFR card scripts 04 Jul 2021-07-04 21:56:30 +01:00
paul_snoops
28db6218b4 AFR card scripts 04 Jul 2021-07-04 21:38:08 +01:00
leriomaggio
146f44f062 Made sets attr protected to allow subclassing 2021-07-04 21:12:11 +01:00
leriomaggio
8977303c00 Improved getCount summing up the number of allowed sets in all selected blocks. 2021-07-04 21:09:38 +01:00
leriomaggio
96fa129285 FIX typo cardBlock attribute was never set in constructor. 2021-07-04 21:04:43 +01:00
leriomaggio
1aa7825c52 New Block CardManager filter (if Load Historic format is enabled)
The new CardBlockFilter extends the generic CardSetFilter by specialising the selection to set codes allowed in the corresponding Block GameFormat.

A new Label `lblBlock` has been added to language file (so far, English only)
2021-07-04 21:01:39 +01:00
leriomaggio
9256fdfd1a FIXED dates comparison for Historic formats, and used index in addition (if no date) 2021-07-04 20:45:26 +01:00
leriomaggio
0ef7986e3a Corrected Set Codes for Masques and Time Spiral Block formats 2021-07-04 20:44:44 +01:00
leriomaggio
8a7848f411 Changed sets to protected to allow SubClassing 2021-07-04 20:44:04 +01:00
tool4EvEr
db3accfa50 Clean up 2021-07-04 20:07:44 +02:00
leriomaggio
a2e97700df Simplified sorting of QuestWorld 2021-07-04 18:31:29 +01:00
leriomaggio
9d80ae2d5c Fixing format types and subtypes read after uppercase change 2021-07-04 18:22:11 +01:00
tool4EvEr
af04a994bb Restore ability 2021-07-04 18:48:47 +02:00
leriomaggio
33526bb31b Updating all enum FormatSubType to UPPERCASE as constants 2021-07-04 17:46:07 +01:00
leriomaggio
9c72cf2eb4 Updating all enum FormatType to UPPERCASE as constants 2021-07-04 17:44:53 +01:00
leriomaggio
81b214f6ed CardManager updated to instantiate quest filters without reprints
CardManager now instantiate Quest World filters forcing a NO-REPRINT policy. The `allowReprints` option was set to true by default for card and deck catalog, resulting in just a weird and unexpected side-effect IMHO. Whenever a block is selected, no other reprints should be taken into account. That's the point of choosing a block.
2021-07-04 17:40:33 +01:00
leriomaggio
eed49554b5 The CardQuestWorldFilter constructors overload to include option to disable reprints.
The CardQuestWorldFilter has been updated with two extra constructors overload that also control the option to include or not the `allowReprints` attribute.
By default, the allowReprints attribute is true for backward compatibility.

The new constructor will be used (next commit) by CardManager when creating filters in Card Catalog.
2021-07-04 17:27:56 +01:00
Hans Mackowiak
5cb384afa2 Grist: Better check for StaticAbility using ExcludeZone, fix timestamp on Stack, and extra rule for canBeCommander 2021-07-04 18:27:31 +02:00
leriomaggio
ef18dac22c CardSetFilter and DeckSetFilter now includes the allowReprints option when instantiating (for edit) the DialogChooseSets so UI updates accordingly. 2021-07-04 17:23:46 +01:00
leriomaggio
8f01f56a1e Updates to DialogChooseSets for UI sizes and status restore
First off, the status of the "allowReprints" checkbox is updated accordingly, when a filter is edited.
This fixes a previous bug that did not take this into account
(more on this, in the next commit msg).

Other improvements to the DialogChooseSets includes a better management of spaces, as well as dimension (width) of main windows that is now dynamically adapted to current resolution.

Also, this update fixes a bug having some panels dynamically adjusting height to fit components.
Now all the components should be correctly aligned, and spread across the panel.
2021-07-04 17:21:26 +01:00
leriomaggio
d8cb67a5f6 A few minor adjustments to allow the UI to restore status after an edit op. 2021-07-04 17:17:41 +01:00
paul_snoops
a08df2b69d AFR card scripts 04 Jul 2021-07-04 14:44:58 +01:00
paul_snoops
4d69748c9f AFR card scripts 04 Jul 2021-07-04 14:41:28 +01:00
tool4EvEr
12be76899a Fix amount for X spells 2021-07-04 13:41:24 +02:00
tool4EvEr
b982620402 Fix RE 2021-07-04 12:21:43 +02:00
tool4EvEr
9a58359563 Better fix for Kethis 2021-07-04 11:45:39 +02:00
Michael Kamensky
233ad71b86 Merge branch 'AffectedAmount' into 'master'
Fix AffectedAmount

See merge request core-developers/forge!4943
2021-07-04 05:04:41 +00:00
Michael Kamensky
bb9cf72912 Merge branch 'AFR_cards' into 'master'
AFR card scripts 03 Jul

See merge request core-developers/forge!4942
2021-07-04 05:04:10 +00:00
leriomaggio
3906c1f3a5 Merge remote-tracking branch 'upstream/master' into editions-type-review 2021-07-04 00:22:10 +01:00
leriomaggio
a5b256f2fb Completely redesigned DialogChooseSets to use FCheckboxTree component.
This new version of the panel is completely interactive, and dynamically populated with card editions, types, and formats.

The selection of formats leads to selections of subsets of editions (and so type), updating other components accordingly.
2021-07-03 21:32:32 +01:00
paul_snoops
12902e8b27 AFR card scripts 03 Jul 2021-07-03 21:31:11 +01:00
leriomaggio
0393c8d4d9 Updated JDocs to workaround IDE errors 2021-07-03 21:30:25 +01:00
leriomaggio
37d7ef3ee9 Brand new desktop UI Component representing a JTree of FCheckboxes
Differently from standard JTree, this component leverages on a custom TreeCellRenderer that renders each node with a checkbox, therefore rewriting completely the selection mechanism of the JTree.

This component is currently used in the DialogChooseSets to represent card editions grouped per edition type.
In general, this component can be used elsewhere, whenever nested objects for selection should be used.

The class exposes a public API for easy interaction, as well as customised classes for nodes and nodes data, and specialised events and listeners.
2021-07-03 21:29:46 +01:00
paul_snoops
1417bf436d AFR card scripts 03 Jul 2021-07-03 21:29:26 +01:00
tool4EvEr
35121c85af Fix AffectedAmount 2021-07-03 22:07:55 +02:00
leriomaggio
c9f0d81d29 Type for Custom editions changed from THIRDPARTY to CUSTOM_SET 2021-07-03 21:04:29 +01:00
leriomaggio
88bba9e7c7 Updated Chinese translations for DialogChooseSets 2021-07-03 21:03:47 +01:00
leriomaggio
8764a4d33d Updated ja-JP (Japanese) translations for DialogChooseSets 2021-07-03 20:59:33 +01:00
leriomaggio
a6d306cb36 Updated it-IT (Italian) translations for DialogChooseSets 2021-07-03 20:54:55 +01:00
leriomaggio
4f0791aac0 Updated es-ES (Spanish) translations for DialogChooseSets 2021-07-03 20:46:37 +01:00
leriomaggio
09e778daeb Updated de-DE (German) translations for DialogChooseSets 2021-07-03 20:43:01 +01:00
leriomaggio
8e3644a0f6 Updated En-US translations for DialogChooseSets 2021-07-03 20:30:31 +01:00
leriomaggio
c3db51b5f8 Updated Javadoc param 2021-07-03 20:14:37 +01:00
paul_snoops
9af01ed521 AFR card scripts 03 Jul 2021-07-03 16:53:58 +01:00
paul_snoops
063cbbfd8a AFR card scripts 03 Jul 2021-07-03 14:36:47 +01:00
Michael Kamensky
ef79f05db3 Merge branch 'cardfixes' into 'master'
Entwine fix

See merge request core-developers/forge!4941
2021-07-03 13:27:06 +00:00
tool4EvEr
8562f2f28d Entwine fix 2021-07-03 15:16:36 +02:00
Michael Kamensky
3e72125163 Merge branch 'play' into 'master'
PlayEffect: small fixes

See merge request core-developers/forge!4939
2021-07-03 04:49:03 +00:00
Michael Kamensky
3e007065b0 Merge branch 'AFR_cards' into 'master'
AFR card scripts 02 Jul

See merge request core-developers/forge!4938
2021-07-03 04:48:38 +00:00
paul_snoops
31fb70f115 AFR cards from various Discord contributors 2021-07-03 03:19:12 +01:00
paul_snoops
7e0e24ddd7 AFR cards from various Discord contributors 2021-07-03 01:19:40 +01:00
tool4EvEr
c52a94bbfc Refactor for facedown 2021-07-03 00:48:05 +02:00
paul_snoops
80aa06cd92 AFR cards from various Discord contributors 2021-07-02 17:34:49 +01:00
paul_snoops
dcd42ef387 AFR cards from various Discord contributors 2021-07-02 17:31:57 +01:00
paul_snoops
7da17c3a36 AFR cards from various Discord contributors 2021-07-02 15:26:26 +01:00
paul_snoops
88afa85e88 AFR cards from various Discord contributors 2021-07-02 11:01:14 +01:00
paul_snoops
16c7add22c AFR cards from various Discord contributers 2021-07-02 09:40:03 +01:00
Michael Kamensky
82339213a8 Merge branch 'master' into 'master'
Added puzzle PS_STX5.

See merge request core-developers/forge!4937
2021-07-02 05:35:55 +00:00
Michael Kamensky
36caca89b6 - Added puzzle PS_STX5. 2021-07-02 08:31:50 +03:00
Michael Kamensky
3ced2f2a24 Merge branch 'afr_dungeon_related_cards' into 'master'
Implement some cards with dungeon mechanism that needs engine change.

See merge request core-developers/forge!4936
2021-07-02 05:03:29 +00:00
Alumi
a12848864b Implement some cards with dungeon mechanism that needs engine change. 2021-07-02 05:03:28 +00:00
Michael Kamensky
e2785d4180 Merge branch 'AFR' into 'master'
AFR update 01 Jul

See merge request core-developers/forge!4935
2021-07-02 03:35:26 +00:00
paul_snoops
1421e1617d AFR update 01 Jul 2021-07-01 18:16:01 +01:00
Michael Kamensky
a45cbff98b Merge branch 'worstland' into 'master'
Update getWorstLand

See merge request core-developers/forge!4933
2021-07-01 03:30:14 +00:00
Michael Kamensky
270c7e3571 Merge branch 'AFR' into 'master'
AFR update 30 Jun

See merge request core-developers/forge!4932
2021-07-01 03:29:03 +00:00
tool4EvEr
1b3cf98372 Update getWorstLand 2021-07-01 00:02:57 +02:00
paul_snoops
b647a74469 AFR update 30 Jun 2021-06-30 16:31:10 +01:00
Michael Kamensky
31d0d4535e Merge branch 'notnamed_comma_workaround' into 'master'
Add the same workaround to `notnamed` property for card name with comma

See merge request core-developers/forge!4931
2021-06-30 03:00:51 +00:00
Lyu Zong-Hong
e15c4be102 Add the same workaround to
otnamed property for card name with comma
2021-06-30 09:52:38 +09:00
Michael Kamensky
846b6de9ab Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!4930
2021-06-29 15:52:25 +00:00
CCTV-1
673f26cab2 translate venture effect strings. 2021-06-29 20:02:44 +08:00
Michael Kamensky
ffb05ed1b6 Merge branch 'twosat-master-patch-37299' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4929
2021-06-29 10:11:45 +00:00
Andreas Bendel
c4eb1ceb10 Update de-DE.properties
translated the two new dungeon strings
2021-06-29 09:56:20 +00:00
Sol
21e27534f8 Update LEG Challenge Arboria.dck 2021-06-29 01:16:19 +00:00
Bug Hunter
b874c202cb Merge branch 'TRT-master-patch-19714' into 'master'
Update forge-gui/res/cardsfolder/f/frostwielder.txt

See merge request core-developers/forge!4928
2021-06-28 12:30:08 +00:00
Bug Hunter
de163b31d8 Update forge-gui/res/cardsfolder/f/frostwielder.txt 2021-06-28 12:28:08 +00:00
Michael Kamensky
b5d93cabd3 Merge branch 'master' into 'master'
Added puzzles PS_KHM6 through PS_STX4.

See merge request core-developers/forge!4927
2021-06-28 09:46:45 +00:00
Michael Kamensky
0d30d8659a - Added puzzles PS_STX3, PS_STX4. Tweaked PS_STX1.
- PS_STX4 needs extra AI logic for Leyline Tyrant (DamageDealAi#chkAIDrawback).
2021-06-28 12:38:18 +03:00
Michael Kamensky
b5bc614c65 - Added puzzle PS_STX2 with GameState support. 2021-06-28 07:22:11 +03:00
Michael Kamensky
ac23ad25fc - Added puzzles PS_KHM6, PS_TSR1, PS_STX1. 2021-06-28 06:44:37 +03:00
Michael Kamensky
510063bd98 Merge branch 'update_to_the_slaughter' into 'master'
Revert To the Slaughter to use StoreSVar to save Delirium mode before resolving

See merge request core-developers/forge!4926
2021-06-28 03:23:53 +00:00
Lyu Zong-Hong
83e65d3b4d Revert To the Slaughter to use StoreSVar to save Delirium mode before resolving 2021-06-28 10:17:42 +09:00
swordshine
2c5548bc88 Merge branch 'smallfix' into 'master'
Small fixes

See merge request core-developers/forge!4925
2021-06-28 00:48:22 +00:00
tool4EvEr
71ab3310da Small fixes 2021-06-27 23:21:11 +02:00
Michael Kamensky
73929b3e1a Merge branch 'fix_sac_each_valid' into 'master'
Update some sacrifice spells that also should use SacEachValid

See merge request core-developers/forge!4923
2021-06-27 13:27:10 +00:00
Michael Kamensky
55536c6480 Merge branch 'cardfixes' into 'master'
Fix keywords

See merge request core-developers/forge!4922
2021-06-27 11:49:17 +00:00
Lyu Zong-Hong
b59ef4e43e Update some cards that needs to sacrifice maximum possible of permanents 2021-06-27 20:47:03 +09:00
tool4EvEr
42696363d3 Fix keywords 2021-06-27 13:01:46 +02:00
Michael Kamensky
85d6dce7f4 Merge branch 'venture_into_the_dungeon' into 'master'
Implement Dungeon mechanism and related spoiled cards

See merge request core-developers/forge!4920
2021-06-27 10:33:56 +00:00
Lyu Zong-Hong
8b66ab3600 Fix SacEachValid for some corner cases 2021-06-27 19:06:37 +09:00
Lyu Zong-Hong
c5e9e0e703 Update oracle texts for Dungeon cards 2021-06-27 15:53:28 +09:00
Lyu Zong-Hong
15852e2c01 Merge branch 'master' into venture_into_the_dungeon 2021-06-27 15:24:57 +09:00
Lyu Zong-Hong
ed0c5aac5e Add localization strings, draw markers, and updated Oubliette room 2021-06-27 15:22:33 +09:00
Michael Kamensky
59e82a33ab - Basic implementation for the Dungeon mechanic AI. 2021-06-27 08:48:49 +03:00
Michael Kamensky
54cbd32229 Merge branch 'fix26' into 'master'
getPossibleReplaceDamageList: Fix NPE

See merge request core-developers/forge!4921
2021-06-27 04:07:20 +00:00
tool4EvEr
ec33e028f6 Fix NPE 2021-06-26 23:31:28 +02:00
Lyu Zong-Hong
be40fce2c6 Implement Dungeon mechanism and related spoiled cards 2021-06-26 18:29:14 +09:00
Michael Kamensky
0d9590f12a Merge branch 'AFR' into 'master'
AFR update 25 Jun

See merge request core-developers/forge!4917
2021-06-26 03:42:23 +00:00
Michael Kamensky
04403d12d7 Merge branch 'fixstuff' into 'master'
Fix Nykthos Paragon

See merge request core-developers/forge!4919
2021-06-26 03:42:16 +00:00
Bug Hunter
96fcd3decd Fix Nykthos Paragon 2021-06-26 03:42:16 +00:00
paul_snoops
5e79ff5a20 AFR update 25 Jun 2021-06-26 00:08:05 +01:00
paul_snoops
9487d84e06 AFR update 25 Jun 2021-06-25 23:03:04 +01:00
Michael Kamensky
6e2a7904bb Merge branch 'master' into 'master'
Update BoosterCovers for recent sets.

See merge request core-developers/forge!4918
2021-06-25 16:22:00 +00:00
Michael Kamensky
a098e659a8 - Update BoosterCovers for recent sets. 2021-06-25 19:20:10 +03:00
leriomaggio
c5eaef01ce Merge remote-tracking branch 'upstream/master' into editions-type-review 2021-06-25 17:13:25 +01:00
leriomaggio
6712a25bfc Updated New Edition type (Promos => Promo) 2021-06-25 17:09:20 +01:00
leriomaggio
bed2d0bddc Merge remote-tracking branch 'upstream/master' into editions-type-review 2021-06-25 17:08:45 +01:00
paul_snoops
ae9e9c5201 AFR update 26 Jun 2021-06-25 16:49:25 +01:00
Michael Kamensky
44910573de Merge branch '1902-kodama-of-the-east-tree-triggers-again-when-you-choose-card-with-etbreplacement' into 'master'
Resolve "Kodama of the East Tree triggers again when you choose card with ETBreplacement"

Closes #1902

See merge request core-developers/forge!4916
2021-06-25 14:54:21 +00:00
Hans Mackowiak
4876fb0212 Fixes Cause for ETBReplacementEffect 2021-06-25 12:16:25 +00:00
Michael Kamensky
9eabe65496 Merge branch 'fix_aerial_caravan' into 'master'
Fix Aerial Caravan

See merge request core-developers/forge!4915
2021-06-24 11:58:41 +00:00
Lyu Zong-Hong
9681ce5f9b Fix Aerial Caravan 2021-06-24 20:10:05 +09:00
Michael Kamensky
8ea07e67f8 Merge branch 'zaffai' into 'master'
Fix wrong calculated CMC for copied spells

See merge request core-developers/forge!4910
2021-06-23 16:32:31 +00:00
Bug Hunter
f281287c69 Fix wrong calculated CMC for copied spells 2021-06-23 16:32:30 +00:00
Michael Kamensky
e5bdcead2a Merge branch 'master' into 'master'
Net Decks Archive Updates

See merge request core-developers/forge!4914
2021-06-23 09:36:48 +00:00
Churrufli
6b22266602 Net Decks Archive Updates 2021-06-23 08:39:34 +02:00
Churrufli
ad41eec543 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2021-06-23 08:34:14 +02:00
Michael Kamensky
1af68d9212 Merge branch 'CollectorBoosters' into 'master'
Collector boosters

See merge request core-developers/forge!4913
2021-06-23 03:44:28 +00:00
friarsol
dc886b139a Add full availability of other booster types 2021-06-22 23:36:12 -04:00
Michael Kamensky
806d8f35c2 Merge branch 'PLG21' into 'master'
Added PLG21 and fixed PLGS>PLG20

See merge request core-developers/forge!4912
2021-06-22 13:40:17 +00:00
paul_snoops
b17e88a80c Added PLG21 and fixed PLGS>PLG20 2021-06-22 12:43:17 +01:00
friarsol
2619cca56f Add support for collector boosters 2021-06-21 21:44:54 -04:00
leriomaggio
2c2f546084 Merge remote-tracking branch 'upstream/master' into editions-type-review 2021-06-21 13:49:49 +01:00
Michael Kamensky
ea138b8c36 Merge branch 'fix-image-fetcher-backface-1882' into 'master'
FIX ImageFetcher BackFace card check

Closes #1882

See merge request core-developers/forge!4911
2021-06-21 12:44:56 +00:00
leriomaggio
5ba6f29ebb FIX ImageFetcher BackFace card check
Quick FIX to restore correct download of backface of cards using directly imageKey string pattern
2021-06-21 13:33:18 +01:00
leriomaggio
79f4595292 Merge remote-tracking branch 'upstream/master' into editions-type-review 2021-06-21 11:45:15 +01:00
leriomaggio
fe0441c667 TEMPORARY new Dialog for Sets filtering (still very WIP) 2021-06-21 11:44:20 +01:00
leriomaggio
3ba464665e New methods returning a map of all editions per type.
This method is used to populate the "Sets" advanced filters in UI (so far, desktop only)
2021-06-21 11:43:51 +01:00
leriomaggio
844360fb34 CardEdition.Type now includes new methods for String representation (UI) 2021-06-21 11:43:01 +01:00
Michael Kamensky
ee7ac95b6a Merge branch 'endurance-library-position' into 'master'
Endurance should send cards to bottom of library

See merge request core-developers/forge!4909
2021-06-21 03:01:20 +00:00
Michael Kamensky
5c46556537 Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!4908
2021-06-21 03:01:16 +00:00
Brock Fanning
70a6a9bda5 Endurance send to bottom of library 2021-06-20 22:05:25 -04:00
Michael Kamensky
a5c30cadb4 Merge branch 'add_remaining_commander_cards' into 'master'
Add remaining missing cards in Commander sets

See merge request core-developers/forge!4907
2021-06-20 10:56:44 +00:00
Bug Hunter
fcdc8d691d Merge branch 'glinting-creeper-blocking-fix' into 'master'
Fix for Glinting Creeper blocking ability and text

See merge request core-developers/forge!4905
2021-06-20 09:00:59 +00:00
Lyu Zong-Hong
6daf4ac78d Add remaining commander cards 2021-06-20 17:22:49 +09:00
CCTV-1
ad72b44e12 translate new strings 2021-06-20 11:52:45 +08:00
Michael Kamensky
d395af122c Merge branch 'add_cruel_entertainment' into 'master'
Add Cruel Entertainment

See merge request core-developers/forge!4906
2021-06-20 03:44:05 +00:00
Lyu Zong-Hong
cb0259c946 Add AI:RemoveDeck:All flag 2021-06-20 12:07:38 +09:00
Lyu Zong-Hong
6e5d79a8e0 Add Cruel Entertainment 2021-06-20 12:00:28 +09:00
Brock Fanning
7d8ea744c3 Fix for Glinting Creeper blocking 2021-06-19 20:55:41 -04:00
Michael Kamensky
8a96bbb3d4 Merge branch 'update_oracle_mh2' into 'master'
Update oracle mh2

See merge request core-developers/forge!4901
2021-06-19 14:54:47 +00:00
Michael Kamensky
36e2c16c9d Merge branch 'vermin-gorger-fix' into 'master'
Fix "Defined" syntax in Vermin Gorger

See merge request core-developers/forge!4903
2021-06-19 14:54:42 +00:00
Michael Kamensky
8928c9eb88 Merge branch 'ozolith' into 'master'
CountersMoveAi: avoid buffing opponent creatures with The Ozolith

See merge request core-developers/forge!4904
2021-06-19 14:54:09 +00:00
tool4EvEr
0de9a0dfd6 CountersMoveAi: avoid buffing opponent creatures with The Ozolith 2021-06-19 16:25:36 +02:00
Brock Fanning
4ad137a23d Fix syntax in Vermin Gorger 2021-06-19 07:11:58 -04:00
Michael Kamensky
f9651796f8 Merge branch 'seal' into 'master'
Fix Seal of the Guildpact

See merge request core-developers/forge!4902
2021-06-19 09:38:46 +00:00
Churrufli
ce02772161 Merge branch 'master' of https://git.cardforge.org/core-developers/forge
# Conflicts:
#	forge-gui/src/main/java/forge/deck/NetDeckCategory.java
#	forge-gui/src/main/java/forge/localinstance/properties/ForgeConstants.java
2021-06-19 11:02:06 +02:00
tool4EvEr
84f2d2ead2 Fix Seal of the Guildpact 2021-06-19 07:28:56 +02:00
Michael Kamensky
d232cefe57 Merge branch 'combomana' into 'master'
Fix ComboMana payment

See merge request core-developers/forge!4900
2021-06-19 04:55:51 +00:00
Lyu Zong-Hong
84882cd4fa Update Ranar the Ever-Watchful and other similar cards 2021-06-19 13:55:02 +09:00
Lyu Zong-Hong
0d592c4096 Update types 2021-06-19 13:40:33 +09:00
Lyu Zong-Hong
c7d7130344 Update oracle for MH2 release 2021-06-19 12:47:36 +09:00
tool4EvEr
7672817a6e Fix ComboMana payment 2021-06-18 23:43:40 +02:00
Michael Kamensky
b23e550263 Merge branch 'TRT-master-patch-16657' into 'master'
Update forge-gui/res/cardsfolder/m/moritte_of_the_frost.txt

See merge request core-developers/forge!4899
2021-06-18 15:43:27 +00:00
Bug Hunter
b1f13cf2f0 Update forge-gui/res/cardsfolder/m/moritte_of_the_frost.txt 2021-06-18 12:47:00 +00:00
Michael Kamensky
afeedf064e Merge branch 'specify_mana_combo' into 'master'
[UI] Use VAssignGenericAmount dialog to specify mana combo

See merge request core-developers/forge!4898
2021-06-18 10:50:50 +00:00
Lyu Zong-Hong
9a21f038ad Not use VAssignGenericAmount dialog for TwoEach ability 2021-06-18 18:28:15 +09:00
Lyu Zong-Hong
afa6c9986a Use VAssignGenericAmount dialog to assign mana combo 2021-06-18 17:58:52 +09:00
Michael Kamensky
c7e489b477 Merge branch 'ui_update_divided_as_you_choose' into 'master'
[UI] Use VAssignGenericAmount dialog to assign DividedAsYouChoose values

See merge request core-developers/forge!4897
2021-06-18 03:10:35 +00:00
Michael Kamensky
bd060e9090 Merge branch 'twosat-master-patch-92329' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4896
2021-06-18 03:09:46 +00:00
Lyu Zong-Hong
dfecf5d40a Use VAssignGenericAmount dialog to assign DividedAsYouChoose values 2021-06-18 11:21:23 +09:00
Andreas Bendel
db3919ab90 Update de-DE.properties
translated lblAssignCombatDamageAsChoose and lblAssignCombatDamageToCreature
2021-06-17 19:11:54 +00:00
Michael Kamensky
5193447625 Merge branch 'sld_update' into 'master'
SLD update

See merge request core-developers/forge!4895
2021-06-17 18:56:21 +00:00
paul_snoops
42c5b890a7 SLD update 2021-06-17 19:24:00 +01:00
Michael Kamensky
31192889d1 Merge branch 'fixes' into 'master'
Minor card fixes

See merge request core-developers/forge!4894
2021-06-17 17:50:21 +00:00
tool4EvEr
b5e55a3a47 Minor card fixes 2021-06-17 17:05:16 +02:00
Michael Kamensky
5e39a2979b Merge branch 'update_damage_replacement_cards' into 'master'
Update some damage replacement related cards

See merge request core-developers/forge!4893
2021-06-17 09:48:53 +00:00
Michael Kamensky
be3c26676e Merge branch 'update_illusionary_terrain' into 'master'
Update Illusionary Terrain and Vision Charm

See merge request core-developers/forge!4892
2021-06-17 09:48:30 +00:00
Alumi
20dc234ec1 Update Illusionary Terrain and Vision Charm 2021-06-17 09:48:30 +00:00
Lyu Zong-Hong
cd29194824 Update some damage replacement related cards 2021-06-17 15:44:12 +09:00
Michael Kamensky
777514dc7d Merge branch 'fixes' into 'master'
Small Card fixes

See merge request core-developers/forge!4891
2021-06-17 02:58:47 +00:00
tool4EvEr
28ee56baea Small Card fixes 2021-06-16 20:02:56 +02:00
Michael Kamensky
cc362cec5c Merge branch 'fix_trigger_damage_map' into 'master'
Fix crash when triggered ability is using damage map

See merge request core-developers/forge!4889
2021-06-16 15:04:26 +00:00
Michael Kamensky
b698b49256 Merge branch 'triggers' into 'master'
AddToCombat: add missing block trigger

See merge request core-developers/forge!4887
2021-06-16 15:04:14 +00:00
Michael Kamensky
0b74be743a Merge branch 'fixes' into 'master'
Minor fixes

See merge request core-developers/forge!4890
2021-06-16 15:04:03 +00:00
Michael Kamensky
d844df988f Merge branch 'misc_card_panel' into 'master'
[Desktop] Add MiscCardPanel to render player avatar when assigning trample damage or divide shields

See merge request core-developers/forge!4888
2021-06-16 15:03:38 +00:00
tool4EvEr
410ceb2bfc Fix facedown reveal 2021-06-16 16:32:43 +02:00
tool4EvEr
da1dbfc9cf Add AI hint 2021-06-16 16:19:34 +02:00
Lyu Zong-Hong
06fa495b0c Fix crash when triggered ability is using damage map by redirect them to the underlying sa 2021-06-16 20:48:50 +09:00
Lyu Zong-Hong
21d5aeca09 Add MiscCardPanel to render player avatar when assigning trample damage or divide shields 2021-06-16 18:14:10 +09:00
leriomaggio
b1134ef070 Updated menu for card filters on mobile with latest Edition Types 2021-06-16 07:26:05 +01:00
leriomaggio
e8b6e066e3 Updated types in CardEdition enum 2021-06-16 07:25:38 +01:00
Michael Kamensky
c7d7f5c622 Merge branch 'paycheat' into 'master'
Fix casting SA while refusing to pay CostSacrifice

See merge request core-developers/forge!4881
2021-06-16 03:15:52 +00:00
Sol
50221ef138 Update Modern.txt 2021-06-16 01:38:54 +00:00
tool4EvEr
d59d5b3b46 AddToCombat: add missing block triggers 2021-06-15 21:26:13 +02:00
Michael Kamensky
6fadafa19a Merge branch 'net_decks' into 'master'
Small net decks update

See merge request core-developers/forge!4886
2021-06-15 17:45:19 +00:00
Michael Kamensky
400eb62c76 Merge branch 'fixes' into 'master'
Small fixes

Closes #1891 and #1890

See merge request core-developers/forge!4885
2021-06-15 17:45:13 +00:00
paul_snoops
68d78acc00 Small net decks update 2021-06-15 17:29:29 +01:00
tool4EvEr
b3fe2a55db Improve AI 2021-06-15 18:15:16 +02:00
tool4EvEr
9697d697bd Multiplayer improvements 2021-06-15 18:09:12 +02:00
tool4EvEr
b1b06dd0f4 Fix Reflect 2021-06-15 17:41:02 +02:00
tool4EvEr
9dada96de3 Clean up 2021-06-15 17:25:00 +02:00
tool4EvEr
93d80b3ee5 Basic SkipPhase AI 2021-06-15 17:01:59 +02:00
tool4EvEr
f584c581d8 Improve AI hint 2021-06-15 16:46:22 +02:00
tool4EvEr
001106ea7b Small fixes 2021-06-15 16:36:33 +02:00
leriomaggio
6f14d1d37a Remapping as per Alumi suggestions in Discord chat
In short, these are the new changes:
- new Boxed set category separated from Reprint
- Reprint now only includes masters editions
- Commander now only includes commander sets
- Multiplayer sets (new) now includes Game Night and Archenemy and
  Planechase
- Game Night mapped to multiplayer
- Myster Boost mapped to Draft innovation
2021-06-15 08:47:25 +01:00
Michael Kamensky
43c39ec231 Merge branch 'update_quest_worlds' into 'master'
Revert worlds.txt back to use only sets intended by the author

See merge request core-developers/forge!4884
2021-06-15 05:24:01 +00:00
Lyu Zong-Hong
9da49cfce1 Revert worlds.txt back to use only sets intended by the author 2021-06-15 14:05:50 +09:00
Michael Kamensky
a3e302eee1 Merge branch 'stuff' into 'master'
Cleanup some cards

See merge request core-developers/forge!4879
2021-06-15 03:30:36 +00:00
Michael Kamensky
5c8fbd3c7e Merge branch 'fix' into 'master'
couple little MH2 fixes

See merge request core-developers/forge!4880
2021-06-15 03:29:59 +00:00
Michael Kamensky
414aa96463 Merge branch 'afr_14' into 'master'
AFR - 14 June

See merge request core-developers/forge!4882
2021-06-15 03:29:49 +00:00
Michael Kamensky
4a25ad9c6a Merge branch 'taxi' into 'master'
Fix Dermotaxi

See merge request core-developers/forge!4883
2021-06-15 03:28:26 +00:00
tool4EvEr
56eda948d6 Fix Dermotaxi 2021-06-15 02:32:13 +02:00
Northmoc
4df7433dfa tashas_hideous_laughter.txt 2021-06-14 18:32:56 -05:00
Northmoc
9cb95f8f35 flumph.txt 2021-06-14 18:32:56 -05:00
tool4EvEr
1c0c0ad38d Fix casting SA while refusing to pay CostSacrifice 2021-06-15 01:28:46 +02:00
Northmoc
84af4d6344 terminal_agony.txt fix cost 2021-06-14 15:37:54 -05:00
Northmoc
d92e2123a2 combine_chrysalis.txt +YouCtrl 2021-06-14 15:35:35 -05:00
tool4EvEr
f447ab64ce Cleanup some cards 2021-06-14 20:45:52 +02:00
leriomaggio
404840de61 Merge remote-tracking branch 'upstream/master' into editions-type-review 2021-06-14 13:14:17 +01:00
Michael Kamensky
4049a3eea3 Merge branch 'fixnpe' into 'master'
Fix NPE when AI can only attack PW without Ultimates

See merge request core-developers/forge!4877
2021-06-14 04:18:34 +00:00
tool4EvEr
36f3165688 Fix NPE 2021-06-13 23:19:34 +02:00
leriomaggio
b79fc195a1 Updaetd PROMO type from (old) PROMOS 2021-06-13 20:39:42 +01:00
leriomaggio
78bc621f3c Sets filter on mobile now consider the new Edition Type groups 2021-06-13 20:24:19 +01:00
leriomaggio
b8aaf8fb68 New Card Types categories also for custom editions
Card Editions now includes updated Types, following the new remapping.

Moreover, a new option to CardEdition.Reader has been added (in the constructor) in order to account for custom editions.

So far this new "reading mode" accounts for type that is "forced" always to be "THIRDPARTY" so to guarantee consistent sets filtering (so far only on Mobile platform).
This new option is integrated in StaticData when instantiating the collection for CustomEditions.
2021-06-13 20:23:34 +01:00
leriomaggio
cd4ce0d3ab Draft Update Editions' Type and Date w/ Scryfall
All Types and Release Date of Forge editions have been updated
according to Scryfall oracle database.
In more details, Scryfall categories have been remapped
so to have the less impact on previous types, whilst
allowing for a better and consistent editions grouping.

This is still WIP and classes (names and matches) can still be
subject to change.
2021-06-13 18:35:09 +01:00
Michael Kamensky
32891863ad Merge branch 'pumpAI' into 'master'
getPumpedCreature: Fix new ID for copy

Closes #1885

See merge request core-developers/forge!4876
2021-06-13 12:49:47 +00:00
Michael Kamensky
77d88b66e8 Merge branch 'smallfixes' into 'master'
Small fixes sequel

See merge request core-developers/forge!4875
2021-06-13 10:59:19 +00:00
tool4EvEr
ff801fe747 Small fixes 2021-06-13 11:31:00 +02:00
tool4EvEr
73016d1fe0 Fix new ID for copy 2021-06-13 11:23:12 +02:00
Michael Kamensky
733962a935 Merge branch 'fixing' into 'master'
Small fixes

See merge request core-developers/forge!4874
2021-06-13 03:54:56 +00:00
tool4EvEr
3f3a90b360 Clean StackDescriptions 2021-06-12 22:22:18 +02:00
tool4EvEr
ec739b40de Fix Fumarole 2021-06-12 22:16:11 +02:00
tool4EvEr
50d3e8cef4 Clean up 2021-06-12 21:58:45 +02:00
tool4EvEr
fa095007cf Small fixes 2021-06-12 21:29:59 +02:00
Michael Kamensky
db3bde4bbb Merge branch 'master' into 'master'
Prepare Forge for Android publish 1.6.42.001 [incremental].

See merge request core-developers/forge!4873
2021-06-12 13:42:42 +00:00
Michael Kamensky
37bb9279f1 - Prepare Forge for Android publish 1.6.42.001 [incremental]. 2021-06-12 16:41:22 +03:00
Michael Kamensky
4e1c6cb678 [maven-release-plugin] prepare for next development iteration 2021-06-12 16:30:00 +03:00
Michael Kamensky
7e1fd332e0 [maven-release-plugin] prepare release forge-1.6.42 2021-06-12 16:29:51 +03:00
Michael Kamensky
58f369e6e5 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2021-06-12 16:25:34 +03:00
Michael Kamensky
368c2f9796 [maven-release-plugin] prepare for next development iteration 2021-06-12 16:13:39 +03:00
Michael Kamensky
d658df35d6 [maven-release-plugin] prepare release forge-1.6.41 2021-06-12 16:13:31 +03:00
Michael Kamensky
4b6edee3e6 Merge branch 'master' into 'master'
Add Planeswalker achievements for MH2 by Marek14

See merge request core-developers/forge!4872
2021-06-12 13:01:44 +00:00
Michael Kamensky
91ae1036de - Add planeswalker achievements for MH2. 2021-06-12 16:00:33 +03:00
Michael Kamensky
c20165df92 Merge branch '1842-panharmonicon-trigger-lingering' into 'master'
Resolve "Panharmonicon trigger lingering"

Closes #1842

See merge request core-developers/forge!4708
2021-06-12 12:57:51 +00:00
Hans Mackowiak
9bdd4b9351 StaticAbilityPanharmonicon: add LastStateBattlefield to Trigger RunParams for stuff that destroys in sequence 2021-06-12 12:57:51 +00:00
Michael Kamensky
c1efe047a3 Merge branch 'announcements-update' into 'master'
Integrations to Announcements.

See merge request core-developers/forge!4870
2021-06-12 12:49:45 +00:00
Michael Kamensky
13e57e25f6 Merge branch 'abilitytext' into 'master'
Fix CantBlockBy ability text not updating

See merge request core-developers/forge!4871
2021-06-12 12:49:19 +00:00
leriomaggio
5345bee294 FIXED typo in Modern Horizons edition name 2021-06-12 12:46:33 +00:00
tool4EvEr
e9b0902fe0 Fix CantBlockBy ability text not updating 2021-06-12 14:44:47 +02:00
leriomaggio
de21053398 Update ANNOUNCEMENTS.txt 2021-06-12 12:43:06 +00:00
leriomaggio
27c993831e Integrations to Announcements. 2021-06-12 13:28:35 +01:00
Sol
3dddca9c1e Merge branch 'paul_snoops-master-patch-02299' into 'master'
Update ANNOUNCEMENTS.txt

See merge request core-developers/forge!4869
2021-06-12 12:05:16 +00:00
Snoops
0effbb805a Update ANNOUNCEMENTS.txt 2021-06-12 10:23:16 +00:00
Michael Kamensky
20225a78cb Merge branch 'paul_snoops-master-patch-68635' into 'master'
Update ANNOUNCEMENTS.txt

See merge request core-developers/forge!4868
2021-06-12 09:27:48 +00:00
Snoops
c8cedbd0af Update ANNOUNCEMENTS.txt 2021-06-12 09:20:29 +00:00
Michael Kamensky
844098a302 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2021-06-12 11:28:43 +03:00
Michael Kamensky
08454489b4 Merge branch 'net_decks' into 'master'
Net decks updates

See merge request core-developers/forge!4861
2021-06-12 08:28:02 +00:00
Michael Kamensky
71110568fc Merge branch 'askai' into 'master'
Add Ask AI button

See merge request core-developers/forge!4867
2021-06-12 04:18:22 +00:00
Michael Kamensky
9990f0ad1a Merge branch 'aiPW' into 'master'
Attack Planeswalker prioritization

See merge request core-developers/forge!4866
2021-06-12 04:17:10 +00:00
Bug Hunter
735a966312 Attack Planeswalker prioritization 2021-06-12 04:17:09 +00:00
tool4EvEr
828d4e17c9 Add Ask AI button 2021-06-11 23:25:57 +02:00
paul_snoops
233c1f6d00 Merge branch 'master' into net_decks 2021-06-11 22:24:22 +01:00
Sol
dbe1683ba9 Merge branch 'zabaz' into 'master'
Zabaz: add Cause to ReplaceAddCounter

See merge request core-developers/forge!4863
2021-06-11 12:29:46 +00:00
Bug Hunter
a5b7f93971 Merge branch 'fixnpe' into 'master'
Fix CostTapType NPE

See merge request core-developers/forge!4865
2021-06-11 10:34:21 +00:00
tool4EvEr
d853ca6615 Fix CostTapType NPE 2021-06-11 12:33:37 +02:00
Michael Kamensky
418d2bf012 Merge branch 'update_card_translations' into 'master'
Update card translations

See merge request core-developers/forge!4862
2021-06-11 10:07:29 +00:00
Lyu Zong-Hong
030f1f4056 Update Japanese translations 2021-06-11 16:56:17 +09:00
Churrufli
45f598e0b9 Oathbreaker Net Decks implementation 2021-06-11 09:07:41 +02:00
Hans Mackowiak
fc82dbb9aa Zabaz: add Cause to ReplaceAddCounter 2021-06-11 08:31:30 +02:00
Lyu Zong-Hong
6feb18c11c Update card translations 2021-06-11 15:10:49 +09:00
Michael Kamensky
c07f3ea34d Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2021-06-11 07:50:33 +03:00
Michael Kamensky
8948210e0a Merge branch 'fixland' into 'master'
Fix playLand ETB check failing if lands changed by another effect

See merge request core-developers/forge!4860
2021-06-11 03:47:55 +00:00
paul_snoops
df0b5362c2 Net decks updates 2021-06-10 20:09:40 +01:00
tool4EvEr
a9b60a87e0 Fix playLand ETB check failing if lands changed by another effect 2021-06-10 18:10:42 +02:00
Sol
694317a50f Merge branch 'fixes' into 'master'
More small fixes

See merge request core-developers/forge!4855
2021-06-10 15:36:25 +00:00
tool4EvEr
a856834693 Refactor guard 2021-06-10 17:29:26 +02:00
Michael Kamensky
35b71da140 Merge branch 'master' into 'master'
Gauntlet Updates

See merge request core-developers/forge!4859
2021-06-10 11:49:55 +00:00
Churrufli
3b2e39808e Gauntlet Updates 2021-06-10 10:41:45 +02:00
Churrufli
697a2ace20 Gauntlet Updates 2021-06-10 10:37:24 +02:00
Sol
c38c53d513 Merge branch 'fix-play-effect' into 'master'
isBlank() is from Java 11

See merge request core-developers/forge!4858
2021-06-10 01:58:13 +00:00
friarsol
31745fe31e isBlank() is from Java 11 2021-06-09 21:57:47 -04:00
Sol
f8e3f4dd31 Merge branch 'migrate-mh2' into 'master'
Migrate Modern Horizons 2

See merge request core-developers/forge!4857
2021-06-10 01:47:40 +00:00
friarsol
965ec4bc47 Migrate Modern Horizons 2 2021-06-09 21:46:33 -04:00
Sol
9cf28e7041 Update u_0_3_crab.txt 2021-06-10 01:28:52 +00:00
Sol
71f639d45e Merge branch 'garth_one_eye' into 'master'
Add Garth One-Eye and necessary support

See merge request core-developers/forge!4856
2021-06-10 00:43:30 +00:00
tool4EvEr
084476b8b0 Clean up 2021-06-09 18:43:34 +02:00
Lyu Zong-Hong
f9abb8ca75 Add Garth One-Eye and necessary support 2021-06-09 23:07:55 +09:00
tool4EvEr
4b0c748eaf Fix Myr Battlesphere allowing you to tap for X = 0 2021-06-09 11:40:11 +02:00
tool4EvEr
083674071b Reset xPaid after ability resolved 2021-06-09 11:40:01 +02:00
tool4EvEr
96ccf0afc6 More small fixes 2021-06-09 09:43:02 +02:00
Michael Kamensky
cfb755ce20 Merge branch 'clean' into 'master'
Small fixes

See merge request core-developers/forge!4854
2021-06-09 05:52:47 +00:00
tool4EvEr
cc8753b563 Small fixes 2021-06-08 23:17:55 +02:00
Michael Kamensky
454c51f1b1 Merge branch 'fix' into 'master'
Various card patches

See merge request core-developers/forge!4831
2021-06-08 14:56:34 +00:00
Michael Kamensky
fa83326710 Merge branch 'mh2_7' into 'master'
MH2 - 7 June

See merge request core-developers/forge!4848
2021-06-08 14:55:48 +00:00
Northmoc
eb60690973 viashino_lashclaw.txt add AILogic 2021-06-08 10:17:43 -04:00
Northmoc
0983c72ea0 viashino_lashclaw.txt 2021-06-08 10:17:42 -04:00
Northmoc
49436a00b3 graceful_restoration.txt 2021-06-08 10:17:42 -04:00
Northmoc
8345bf890b glimmer_bairn.txt 2021-06-08 10:17:42 -04:00
Northmoc
5b2980562b emergent_growth.txt clean up stackdesc 2021-06-08 10:16:03 -04:00
Northmoc
b86249635f drake_stone.txt fix ManaCost 2021-06-08 10:16:03 -04:00
Northmoc
4286429402 gravebreaker_lamia.txt add Mandatory 2021-06-08 10:16:02 -04:00
Michael Kamensky
6578fb79e6 Merge branch 'master' into 'master'
Net Decks Archive Updates - New Gauntlets

See merge request core-developers/forge!4852
2021-06-08 10:33:26 +00:00
Bug Hunter
f1169511ab Merge branch 'TRT-master-patch-20578' into 'master'
Update forge-gui/res/cardsfolder/b/bog_rats.txt

Closes #1883

See merge request core-developers/forge!4853
2021-06-08 07:33:29 +00:00
Bug Hunter
6f5f04bb7e Update forge-gui/res/cardsfolder/b/bog_rats.txt 2021-06-08 07:32:14 +00:00
Churrufli
b33595d840 Net Decks Archive Updates - New Gauntlets 2021-06-08 09:25:52 +02:00
Churrufli
653b484674 Net Decks Archive Updates - New Gauntlets 2021-06-08 09:16:34 +02:00
Michael Kamensky
5a0a63940d Merge branch 'typefix' into 'master'
Fix missing type

See merge request core-developers/forge!4849
2021-06-08 03:31:57 +00:00
Michael Kamensky
d734fdf761 Merge branch 'fixnpe' into 'master'
Fix NPE with Grakmaw when run from ChooseType

See merge request core-developers/forge!4850
2021-06-08 03:31:41 +00:00
tool4EvEr
3ab7fcf224 Fix NPE with Grakmaw when run from ChooseType 2021-06-07 22:40:16 +02:00
tool4EvEr
4f4fe04683 Fix missing type 2021-06-07 22:08:21 +02:00
Michael Kamensky
c83d03a2d6 Merge branch 'H1R' into 'master'
RMH1 to H1R fix

See merge request core-developers/forge!4847
2021-06-07 18:28:45 +00:00
paul_snoops
8ffa95a26a RMH1 has been changed to H1R 2021-06-07 16:31:39 +01:00
paul_snoops
da96b580a4 RMH1 has been changed to H1R 2021-06-07 16:28:29 +01:00
paul_snoops
5a862ab742 RMH1 has been changed to H1R 2021-06-07 15:18:51 +01:00
Michael Kamensky
7e6f077b0a Merge branch 'TRT-master-patch-82400' into 'master'
Update forge-gui/res/cardsfolder/upcoming/search_the_premises.txt

See merge request core-developers/forge!4846
2021-06-07 08:02:42 +00:00
Bug Hunter
b6676c9d58 Update forge-gui/res/cardsfolder/upcoming/search_the_premises.txt 2021-06-07 04:56:28 +00:00
Michael Kamensky
37099156f3 Merge branch 'lion' into 'master'
Diamond Lion: Fix cost

See merge request core-developers/forge!4845
2021-06-07 04:32:08 +00:00
tool4EvEr
018a2c080b Fix cost 2021-06-06 21:46:27 +02:00
Michael Kamensky
890ef37b3f Merge branch 'snowblind' into 'master'
Card fixes

See merge request core-developers/forge!4843
2021-06-06 18:04:04 +00:00
Michael Kamensky
c7de3866c7 Merge branch 'PLIST' into 'master'
Edition updates PLIST and UST

See merge request core-developers/forge!4844
2021-06-06 18:03:49 +00:00
tool4EvEr
4cf11743f2 Fix P/T 2021-06-06 19:36:40 +02:00
tool4EvEr
3ee3a28fd5 Fix cost 2021-06-06 18:54:21 +02:00
paul_snoops
39ba048d07 Updating "The List" edition file and fix dragon token in Unstable 2021-06-06 16:55:56 +01:00
tool4EvEr
40a9229681 Fix check 2021-06-06 15:19:23 +02:00
tool4EvEr
8a7873d0c9 Fix triggers 2021-06-06 15:11:34 +02:00
Bug Hunter
996ddb4360 Merge branch 'twosat-master-patch-69468' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4842
2021-06-06 12:57:58 +00:00
tool4EvEr
eec11c6ec5 Fix Snowblind 2021-06-06 14:57:24 +02:00
Andreas Bendel
197cbb1808 Update de-DE.properties
translated new lines under VAssignGenericAmount
2021-06-06 12:51:06 +00:00
Michael Kamensky
32df7f3eae Merge branch 'fixsuspend' into 'master'
Suspend Fixes

See merge request core-developers/forge!4828
2021-06-06 03:29:09 +00:00
Bug Hunter
4b3839b143 Suspend Fixes 2021-06-06 03:29:08 +00:00
Michael Kamensky
79216eaa8d Merge branch 'mh2_fix' into 'master'
MH2 fixes

See merge request core-developers/forge!4841
2021-06-06 03:28:46 +00:00
Michael Kamensky
ba2998541d Merge branch 'aicleanup' into 'master'
Small fix

See merge request core-developers/forge!4840
2021-06-06 03:28:26 +00:00
Michael Kamensky
d5aaa4dc45 Merge branch 'repeateach' into 'master'
RepeatPlayers - Swap out other remembered while resolving

See merge request core-developers/forge!4834
2021-06-06 03:27:03 +00:00
Bug Hunter
999e4f81ee RepeatPlayers - Swap out other remembered while resolving 2021-06-06 03:27:02 +00:00
Michael Kamensky
c38d2f531c Merge branch 'carth' into 'master'
MH2: Carth the Lion

See merge request core-developers/forge!4760
2021-06-06 03:26:09 +00:00
Michael Kamensky
27d8af3551 Merge branch 'carth' into 'master'
Carth support

See merge request core-developers/forge!4827
2021-06-06 03:26:07 +00:00
Bug Hunter
b10ce1b884 Carth support 2021-06-06 03:26:07 +00:00
Northmoc
18c207f39b arcbound_tracker.txt trim unneeded 2021-06-05 17:03:32 -04:00
tool4EvEr
898df454d5 Small fix 2021-06-05 23:02:34 +02:00
Northmoc
577c5ca055 restore edition labels 2021-06-05 17:01:07 -04:00
Northmoc
04558e93b3 correct lizard name 2021-06-05 17:00:44 -04:00
Michael Kamensky
6b0a88cd95 Merge branch 'phpr-prm-scryfall-editions' into 'master'
Latest Scryfall codes for PHPR and PRM Editions

See merge request core-developers/forge!4838
2021-06-05 19:03:02 +00:00
Michael Kamensky
bd125df278 Merge branch 'smallfix' into 'master'
Small fixes

See merge request core-developers/forge!4837
2021-06-05 19:02:53 +00:00
tool4EvEr
fc31881150 Small fixes 2021-06-05 20:52:59 +02:00
leriomaggio
9a13b06452 Matched with Magic Online Promo 2021-06-05 19:44:24 +01:00
leriomaggio
01e3a726fe Matched all with PHPR Set on Scryfall 2021-06-05 19:44:11 +01:00
leriomaggio
bcaa318941 Matched with Dragon Con on Scryfall 2021-06-05 19:43:46 +01:00
leriomaggio
c64e5c2d5e Merge remote-tracking branch 'upstream/master' into add-scryfallcodes-neweditions 2021-06-05 19:40:42 +01:00
Bug Hunter
caafcf61aa Merge branch 'city' into 'master'
Fix crash with City in a Bottle & Facedown

See merge request core-developers/forge!4836
2021-06-05 18:22:27 +00:00
tool4EvEr
e6280377ce Fix crash with City in a Bottle & Facedown 2021-06-05 20:22:30 +02:00
Bug Hunter
a2e66550bf Merge branch 'add-scryfallcodes-neweditions' into 'master'
Scryfall Code Added

See merge request core-developers/forge!4835
2021-06-05 18:21:31 +00:00
leriomaggio
abad45f874 Scryfall Code Added
(Not really needed, just to keep edition files aligned)
2021-06-05 18:21:31 +00:00
leriomaggio
fa649c1cd0 Scryfall Code Added
(Not really needed, just to keep edition files aligned)
2021-06-05 19:10:39 +01:00
leriomaggio
96ad12a1b7 FIX collector numbers w/ those on scryfall and scryfallcode added 2021-06-05 19:10:08 +01:00
Michael Kamensky
669979531c Merge branch 'patch-imagefetcher-scryfall' into 'master'
FIX performance with ImageFetcher, New ScryfallCodes, and Updated CollectorNumbers

See merge request core-developers/forge!4823
2021-06-05 16:25:51 +00:00
leriomaggio
0c81f5c36c FIX unicode char for alternate in Portal 2021-06-05 17:16:44 +01:00
leriomaggio
7dfc48744c Merge remote-tracking branch 'upstream/master' into patch-imagefetcher-scryfall 2021-06-05 17:00:53 +01:00
leriomaggio
943d95eed0 Added new scryfall codes to latest 67 editions 2021-06-05 16:58:07 +01:00
leriomaggio
4b6e5e32d1 Setup Scryfall Code 2021-06-05 16:54:22 +01:00
leriomaggio
a1132fbe90 Updated latest collector numbers 2021-06-05 16:54:05 +01:00
Michael Kamensky
ea37370738 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2021-06-05 18:13:54 +03:00
Michael Kamensky
3b73f86e1b Merge branch 'Williams-master-patch-27805' into 'master'
MH2 Contributions 04/06

See merge request core-developers/forge!4822
2021-06-05 15:11:24 +00:00
Michael Kamensky
1633dedee3 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2021-06-05 17:34:06 +03:00
Michael Kamensky
9ae41c67a2 Merge branch 'replace_damage_missing_cards' into 'master'
Implement remaining damage prevention/redirection cards

See merge request core-developers/forge!4832
2021-06-05 14:28:35 +00:00
Michael Kamensky
58819f48bc Merge branch 'mh2_token' into 'master'
MH2 token issues

See merge request core-developers/forge!4833
2021-06-05 14:27:22 +00:00
Michael Kamensky
1f2cc92533 Merge branch 'phasing' into 'master'
Phasing Fixes

Closes #531

See merge request core-developers/forge!4808
2021-06-05 14:27:11 +00:00
leriomaggio
2e550b95ca New Card Language option to support fetching cards in different languages 2021-06-05 15:21:49 +01:00
leriomaggio
6f27a0db4d ImageFetcher now gets language code directly from CardEdition and not from current locale 2021-06-05 15:17:32 +01:00
leriomaggio
ace7998c40 Removed unused method 2021-06-05 15:17:10 +01:00
leriomaggio
969d65bbb2 Setup card language 2021-06-05 15:06:46 +01:00
leriomaggio
dd3c6abdd2 FIX collector number with unicode characters 2021-06-05 15:06:26 +01:00
leriomaggio
45c50c1f6e Set card language 2021-06-05 14:54:10 +01:00
leriomaggio
93d284dc82 Updated collector numbers with unicode characters 2021-06-05 14:54:00 +01:00
leriomaggio
9f6269c3e8 Updated collector numbers 2021-06-05 14:53:25 +01:00
leriomaggio
54ed9cfe22 Updated collector numbers 2021-06-05 14:53:21 +01:00
Northmoc
efe0b7ace0 u_0_3_crab.txt add newline 2021-06-05 08:27:03 -04:00
Northmoc
96eeac14ea u_0_3_crab.txt 2021-06-05 08:26:46 -04:00
Northmoc
85409f1616 fix golem token 2021-06-05 08:26:26 -04:00
Lyu Zong-Hong
f7f020cc26 Update language files 2021-06-05 21:02:50 +09:00
Northmoc
889173a682 move PutCounter cost earlier 2021-06-05 08:02:27 -04:00
Northmoc
e2fab353fd carth_the_lion.txt 2021-06-05 08:02:26 -04:00
leriomaggio
41a0769cca Updated **all** collector numbers 2021-06-05 12:05:47 +01:00
Tim Mocny
f73fac0eb4 Merge branch 'fixdroid' into 'master'
hotfix for Android

See merge request core-developers/forge!4830
2021-06-05 11:02:46 +00:00
Michael Kamensky
3047e40116 Merge branch 'Williams-master-patch-76451' into 'master'
Vintage/Legacy Championship "Promos", MH2 tokens

See merge request core-developers/forge!4829
2021-06-05 11:00:19 +00:00
Northmoc
24c45c7cc2 hotfix for Android 2021-06-05 06:59:27 -04:00
John
88b2ce84d6 Update Modern Horizons 2.txt 2021-06-05 10:23:38 +00:00
leriomaggio
9c55543f1b Updated card language 2021-06-05 11:21:31 +01:00
leriomaggio
76eac28192 Updated card collector numbers 2021-06-05 11:21:24 +01:00
leriomaggio
b3d2c28f06 Updated card collector numbers and ScryfallCode 2021-06-05 11:19:23 +01:00
leriomaggio
a4c0a56734 Updated card language with Japanese 2021-06-05 11:15:28 +01:00
leriomaggio
fc78637d2f Updated collector numbers and scryfallcode 2021-06-05 11:13:30 +01:00
leriomaggio
7605706b89 Updated collector numbers with unicode characters 2021-06-05 11:09:09 +01:00
leriomaggio
862f3e4e9d Updated collector numbers 2021-06-05 11:01:54 +01:00
leriomaggio
64de8327a6 Updated collector numbers 2021-06-05 11:00:26 +01:00
John
c8d5624600 Add new file 2021-06-05 09:58:31 +00:00
leriomaggio
abe2d7d10a Updated Card language and collector numbers 2021-06-05 10:58:17 +01:00
leriomaggio
27dbddac94 Updated Card language 2021-06-05 10:55:58 +01:00
John
cfd18eef5c Add new file 2021-06-05 09:55:08 +00:00
leriomaggio
e2b30b8f71 Extended Regexp to support Historic Anthology 5 CN types
Historic Anthology 5 introduced a new "type" of collector number, namely
"999-CH". Regexp has been extended to also include those cases.
2021-06-05 10:50:16 +01:00
leriomaggio
0f86978d8a Updated Collector Numbers with Scryfall 2021-06-05 10:49:09 +01:00
leriomaggio
1e7c21bdcf Updated Collector Numbers with Scyfall 2021-06-05 10:49:00 +01:00
leriomaggio
9e8fa209d5 FIX collector numbers with unicode characters 2021-06-05 10:48:47 +01:00
John
83caef1a97 Update vermin_gorger.txt 2021-06-05 09:47:53 +00:00
John
056daed06b Update crack_open.txt 2021-06-05 09:47:30 +00:00
Lyu Zong-Hong
ab31c8fa9d Implement missing cards with dividable shields for damage replacement effects 2021-06-05 18:20:02 +09:00
Lyu Zong-Hong
f95fb7d08b Choose source for prevent/redirect next N damage effect 2021-06-05 18:20:01 +09:00
leriomaggio
b2939f511d FIX collector numbers with unicode characters 2021-06-05 09:15:50 +01:00
leriomaggio
e1a08629a4 Updated collector numbers 2021-06-05 09:15:35 +01:00
leriomaggio
9c5eba7372 Updated collector numbers 2021-06-05 09:06:20 +01:00
leriomaggio
97ecf45da3 FIX collector number with unicode character 2021-06-05 09:05:05 +01:00
leriomaggio
cf4321492e FIX collector number with unicode character 2021-06-05 08:36:13 +01:00
leriomaggio
d5f996d249 added card language 2021-06-05 08:34:12 +01:00
leriomaggio
dc65f639c7 FIX collector number with unicode characters 2021-06-05 08:33:58 +01:00
leriomaggio
64aa9fe0ae Set Scryfall Code 2021-06-05 08:30:00 +01:00
leriomaggio
73f65505b0 Set Card language to Japanese (ja) 2021-06-05 08:28:14 +01:00
leriomaggio
49dc938eb3 FIX collector numbers with unicode characters 2021-06-05 08:25:30 +01:00
leriomaggio
687b547b47 FIX collector numbers with unicode characters 2021-06-05 08:23:08 +01:00
leriomaggio
4b85ef2a99 New CardLang Option
Certain Cards are only available in a specific language (e.g. JP, HE).
This option will be read into card Edition and used by content downloader later on.
2021-06-05 08:22:01 +01:00
leriomaggio
be7ef159cd FIX collector numbers with unicode characters 2021-06-05 08:21:04 +01:00
leriomaggio
8b6f0fb8eb FIX collector numbers with unicode characters 2021-06-05 08:13:15 +01:00
leriomaggio
ef29c61fb0 FIX collector numbers with unicode characters 2021-06-05 08:11:33 +01:00
leriomaggio
77ba81d621 FIX collector numbers with unicode characters 2021-06-05 08:08:25 +01:00
leriomaggio
4d645f761c FIX collector numbers with unicode characters 2021-06-05 08:06:06 +01:00
leriomaggio
b0aeaaabb4 FIX collector numbers with unicode characters 2021-06-05 08:05:02 +01:00
leriomaggio
80661e1646 FIX collector number to a few more missing 2021-06-05 08:03:57 +01:00
Michael Kamensky
f53e4495de Merge branch 'thrasta' into 'master'
MH2: Thrasta and "Trample over planeswalkers"

See merge request core-developers/forge!4824
2021-06-05 03:17:27 +00:00
Michael Kamensky
76ee1e776f Merge branch 'mh2_draft' into 'master'
MH2 limited

See merge request core-developers/forge!4814
2021-06-05 03:16:49 +00:00
Michael Kamensky
81c880efb1 Merge branch 'mh2_3' into 'master'
MH2 - 3 June

See merge request core-developers/forge!4816
2021-06-05 03:16:25 +00:00
Northmoc
3b3c955e2c echoing_return.txt 2021-06-04 15:57:53 -04:00
Northmoc
d786509a06 vile_entomber.txt 2021-06-04 15:31:12 -04:00
Northmoc
7e682810fb final tweaks for now 2021-06-04 15:11:39 -04:00
leriomaggio
446fc7dc35 Enhancing regexp to also support CN with symbol in front (e.g. *180 in Theros promos) 2021-06-04 19:55:16 +01:00
leriomaggio
76ccc2f8b1 Updating collector numbers with unicode chars for matching on scryfall 2021-06-04 19:54:33 +01:00
leriomaggio
6f99e6b840 updating collector numbers with unicode in Arabian nights 2021-06-04 19:54:10 +01:00
tool4EvEr
08d6b11add Clean up 2021-06-04 20:27:44 +02:00
tool4EvEr
dccf161d54 Fix Untap of card that should't after phasing in 2021-06-04 20:27:44 +02:00
tool4EvEr
c648a8ccbc Stop counting phased out cards 2021-06-04 20:27:43 +02:00
tool4EvEr
83dc9b688b Fix for phasing 2021-06-04 20:27:42 +02:00
tool4EvEr
2d53623ab2 Fix echo cost not skipable with phasing 2021-06-04 20:27:41 +02:00
tool4EvEr
d0d14b9006 Fix missing trigger when phased out 2021-06-04 20:27:41 +02:00
tool4EvEr
db85958008 Fix effects on phased out cards 2021-06-04 20:27:40 +02:00
tool4EvEr
58866b0595 when phasing out end Until 2021-06-04 20:27:39 +02:00
tool4EvEr
f6f2d5e4de Fix Attachments not phasing back in when the card attached to does 2021-06-04 20:27:39 +02:00
Bug Hunter
118aa4bda1 Merge branch 'payfix' into 'master'
Better fix for manapool payment

See merge request core-developers/forge!4826
2021-06-04 18:26:10 +00:00
tool4EvEr
ba9b4617f7 Better fix for manapool payment 2021-06-04 20:25:05 +02:00
Bug Hunter
a3646e83c8 Merge branch 'cleanimport' into 'master'
Clean import

See merge request core-developers/forge!4825
2021-06-04 17:24:15 +00:00
tool4EvEr
b51b8e9c63 Clean import 2021-06-04 19:24:22 +02:00
Northmoc
3638372602 nykthos_paragon.txt ai hint 2021-06-04 13:04:08 -04:00
Northmoc
647a0d8a2f brown acorns 2021-06-04 13:02:58 -04:00
Northmoc
0bcaf9cb41 chitterspitter.txt (Suthro) 2021-06-04 13:02:58 -04:00
Northmoc
52c6fb5d06 flourishing_strike.txt (Suthro) 2021-06-04 13:02:57 -04:00
Northmoc
a1a192946c phantasmal_dreadmaw.txt fix Oracle 2021-06-04 13:02:57 -04:00
Northmoc
713b199f21 resurgent_belief.txt 2021-06-04 13:02:56 -04:00
Northmoc
2364e8dd26 Combat.java set up defending planeswalker and controller to receive damage with trample over planeswalkers 2021-06-04 13:00:22 -04:00
Northmoc
60f98c84d5 make sure damage assign window pops up for trampling over planeswalkers 2021-06-04 13:00:21 -04:00
Northmoc
dfea87ed0e properly calculate lethal for planeswalkers being trampled over 2021-06-04 13:00:20 -04:00
Northmoc
3010657620 handle Trample over planeswalkers keyword 2021-06-04 13:00:20 -04:00
Northmoc
35477a5edb Trample over planeswalkers reminder text 2021-06-04 13:00:20 -04:00
Northmoc
793bb97909 thrasta_tempests_roar.txt 2021-06-04 13:00:19 -04:00
Northmoc
ca4e9c2ea5 add "alternate frame" and "bundle" to EditionSectionWithCollectorNumbers, tweak Edition 2021-06-04 12:59:07 -04:00
Northmoc
fa68e989c4 add preliminary MH2 rankings 2021-06-04 12:59:07 -04:00
Northmoc
47291206ae 36 packs in box 2021-06-04 12:59:06 -04:00
Northmoc
77578174b7 - improve booster 2021-06-04 12:59:06 -04:00
Northmoc
f0f7b9e6e4 Modern Horizons 2.txt boosters (initial attempt) 2021-06-04 12:59:05 -04:00
Northmoc
dbb87a6a79 blocks.txt add MH2 2021-06-04 12:59:05 -04:00
John
8d8c1c505c Add new file 2021-06-04 16:47:16 +00:00
John
970db2b6d1 Add new file 2021-06-04 16:46:46 +00:00
John
330e0818fa Add new file 2021-06-04 16:46:15 +00:00
John
7f4f48aa32 Add new file 2021-06-04 16:45:39 +00:00
John
fb4300fe4d Add new file 2021-06-04 16:44:00 +00:00
John
4e89b94916 Add new file 2021-06-04 16:43:09 +00:00
John
0f76d42b8c Add new file 2021-06-04 16:42:39 +00:00
John
6cc3060dc2 Add new file 2021-06-04 16:41:22 +00:00
John
83d629c397 Add new file 2021-06-04 16:40:44 +00:00
John
b7fe9ba794 Add new file 2021-06-04 16:40:08 +00:00
John
c0594c5449 Add new file 2021-06-04 16:38:42 +00:00
John
0eb562adf9 Add new file 2021-06-04 16:30:37 +00:00
John
181bd88902 Add new file 2021-06-04 16:28:11 +00:00
John
3d6cfd2457 Add new file 2021-06-04 16:26:40 +00:00
John
bcb5191ecc Add new file 2021-06-04 16:26:08 +00:00
John
5581283c52 Add new file 2021-06-04 16:25:37 +00:00
John
c3cb530aa1 Add new file 2021-06-04 16:25:01 +00:00
John
5cc0665a9d Add new file 2021-06-04 15:58:32 +00:00
John
6c0dde8e79 Add new file 2021-06-04 15:56:52 +00:00
John
a009e06fdf Add new file 2021-06-04 15:55:52 +00:00
John
4282169fc9 Add new file 2021-06-04 15:55:14 +00:00
John
7c76e72a7f Add new file 2021-06-04 15:54:41 +00:00
John
e22e514d29 Update necrogoyf.txt 2021-06-04 15:52:33 +00:00
John
edfbc6ac95 Update wrens_run_hydra.txt 2021-06-04 15:52:13 +00:00
Michael Kamensky
a23a1fa928 Merge branch 'handcount' into 'master'
Fix full fizzling for targeted players & SA

See merge request core-developers/forge!4817
2021-06-04 15:39:04 +00:00
John
8b2bb48dbc Add new file 2021-06-04 15:34:24 +00:00
John
930183b0aa Add new file 2021-06-04 15:33:36 +00:00
John
bfbf1c5e71 Add new file 2021-06-04 15:32:27 +00:00
John
67684ee0e6 Add new file 2021-06-04 15:31:22 +00:00
John
336ccd2ec4 Add new file 2021-06-04 14:34:15 +00:00
John
21f4a79dd5 Upload New File 2021-06-04 14:33:37 +00:00
John
3e94b2854c Upload New File 2021-06-04 14:33:06 +00:00
leriomaggio
9539060404 Merge remote-tracking branch 'upstream/master' into patch-imagefetcher-scryfall 2021-06-04 15:20:59 +01:00
leriomaggio
9f73524dd9 ImageFetcher now uses scryfallCode instead of testing URLs to fetch images. 2021-06-04 15:20:44 +01:00
leriomaggio
321360e180 Added new ScryfallCode to CardEdition (used by ImageFetcher) 2021-06-04 15:20:21 +01:00
leriomaggio
8ac3d0a2a6 Adding new ScryfallCode for all the 404 out of 486 editions found on Scryfall
There are still 82 editions to be matched manually.
2021-06-04 15:06:36 +01:00
John
b80118c6d0 Update specimen_collector.txt 2021-06-04 12:56:37 +00:00
John
d795812fd4 Update specimen_collector.txt 2021-06-04 12:44:45 +00:00
John
aa945e491c Add new file 2021-06-04 12:41:22 +00:00
John
1e566808cb Add new file 2021-06-04 12:40:32 +00:00
John
6eaa1e9edd Add new file 2021-06-04 12:39:50 +00:00
John
a2d53ce217 Add new file 2021-06-04 12:38:56 +00:00
John
39707005bc Add new file 2021-06-04 12:38:20 +00:00
John
dc8d24322f Add new file 2021-06-04 12:37:49 +00:00
John
912ee35721 Add new file 2021-06-04 12:37:18 +00:00
John
9321eea8f8 Add new file 2021-06-04 12:36:26 +00:00
John
347503d96f recalibrate 2021-06-04 12:34:55 +00:00
swordshine
fca3aade63 Merge branch 'restartg' into 'master'
RestartGameEffect: Reset extras turns

See merge request core-developers/forge!4821
2021-06-04 11:19:43 +00:00
tool4EvEr
a2cd1fa8ef Reset extras after restart 2021-06-04 12:46:40 +02:00
Michael Kamensky
d9db3a6edf Merge branch 'fixpay' into 'master'
Fix source of manapool for payment of second human

See merge request core-developers/forge!4815
2021-06-04 03:55:22 +00:00
Michael Kamensky
1d749eba48 Merge branch 'entwine' into 'master'
Entwine reminder text

See merge request core-developers/forge!4818
2021-06-04 03:55:02 +00:00
Michael Kamensky
22917b7e0a Merge branch 'caprichrome' into 'master'
MH2: Caprichrome with necessary Devour keyword tweaks

See merge request core-developers/forge!4811
2021-06-04 03:54:47 +00:00
Michael Kamensky
e4ca35cae8 Merge branch 'fix' into 'master'
tiamat.txt fix

See merge request core-developers/forge!4820
2021-06-04 03:54:13 +00:00
Michael Kamensky
0c74ee2602 Merge branch 'cardupdates' into 'master'
Small card updates

See merge request core-developers/forge!4819
2021-06-04 03:53:59 +00:00
Michael Kamensky
40e533d9d8 Merge branch 'Williams-master-patch-03324' into 'master'
MH2 Contributions 02/06

See merge request core-developers/forge!4807
2021-06-04 03:53:22 +00:00
Northmoc
01f4bb9494 tiamat.txt fix 2021-06-03 18:38:31 -04:00
tool4EvEr
e386c8e87c Update X 2021-06-03 23:43:32 +02:00
tool4EvEr
bf3b1b03e8 Update card 2021-06-03 23:18:41 +02:00
tool4EvEr
a69c1534f6 Small card updates 2021-06-03 22:22:08 +02:00
Northmoc
41671d7acf caprichrome.txt add AI hints 2021-06-03 16:18:16 -04:00
Northmoc
a909fc1fa8 CardFactoryUtil expand Devour keyword possibilities 2021-06-03 16:18:15 -04:00
Northmoc
919a49b22e Card.java override reminder text for Devouring non-creature stuff 2021-06-03 16:18:15 -04:00
Northmoc
9ae599cf05 caprichrome.txt 2021-06-03 16:18:14 -04:00
Northmoc
5aad37c613 Entwine reminder text 2021-06-03 16:07:29 -04:00
tool4EvEr
69e580e37d Fix full fizzling 2021-06-03 21:09:16 +02:00
tool4EvEr
c735195d33 Fix source of manapool for payment of second human 2021-06-03 19:16:27 +02:00
John
857f0d2dcc Update wavesifter.txt 2021-06-03 14:41:12 +00:00
John
9b6e1f4c64 Update lazotep_chancellor.txt 2021-06-03 14:40:51 +00:00
John
8a41b35317 Update foundry_helix.txt 2021-06-03 14:40:30 +00:00
Michael Kamensky
f08cb0f0b5 Merge branch 'activation' into 'master'
ActivationTable: Fix NPE for triggers without OverridingAbility

Closes #1859

See merge request core-developers/forge!4799
2021-06-03 14:07:15 +00:00
Michael Kamensky
bbf1e373ad Merge branch 'fixstuff' into 'master'
Some smaller fixes

Closes #1868

See merge request core-developers/forge!4806
2021-06-03 14:02:50 +00:00
tool4EvEr
069403ab6c Clean up 2021-06-03 14:22:56 +02:00
Tim Mocny
73ac435b92 Merge branch 'editions' into 'master'
MH2 and RMH1 updates

See merge request core-developers/forge!4812
2021-06-03 12:13:05 +00:00
Tim Mocny
4b439dc7bb Merge branch 'boon' into 'master'
skyblades_boon.txt fix

See merge request core-developers/forge!4813
2021-06-03 12:12:44 +00:00
Northmoc
4c7879f423 skyblades_boon.txt fix 2021-06-03 08:10:51 -04:00
tool4EvEr
fc7540ed1e Fix multiplayer rulings (count player who lost) 2021-06-03 11:52:37 +02:00
tool4EvEr
08fd9b98c8 Fix missing sub 2021-06-03 11:19:50 +02:00
tool4EvEr
1af8cee719 Fix crash on payment with Channel 2021-06-03 10:39:50 +02:00
John
ca6c9c454d Add new file 2021-06-03 08:25:22 +00:00
John
8adcc14422 Add new file 2021-06-03 08:24:56 +00:00
John
65777750ab Add new file 2021-06-03 08:24:31 +00:00
John
96b437c0b4 Add new file 2021-06-03 08:24:01 +00:00
John
d3ae5c38d9 Add new file 2021-06-03 08:23:26 +00:00
John
c6452b2fea Add new file 2021-06-03 08:23:01 +00:00
John
caf1900484 Add new file 2021-06-03 08:22:30 +00:00
John
2e79d34f75 Add new file 2021-06-03 08:22:03 +00:00
John
28566c4272 Add new file 2021-06-03 08:21:34 +00:00
John
28fd2d18fa Add new file 2021-06-03 08:20:44 +00:00
John
df7ce97d40 Add new file 2021-06-03 08:20:16 +00:00
John
5b2504ba3f Add new file 2021-06-03 08:19:51 +00:00
John
cbf4d17c7f Add new file 2021-06-03 08:18:35 +00:00
John
642ba05dcf Add new file 2021-06-03 08:18:08 +00:00
John
b2b5e87aed Add new file 2021-06-03 08:17:47 +00:00
John
3562f3192f Add new file 2021-06-03 08:17:19 +00:00
John
e58d8b35b8 Add new file 2021-06-03 08:16:53 +00:00
John
2761a3778a Add new file 2021-06-03 08:16:21 +00:00
John
1eaa8d9b67 Add new file 2021-06-03 08:15:57 +00:00
John
169b292301 Add new file 2021-06-03 08:15:21 +00:00
John
249cfdb78d Add new file 2021-06-03 08:14:53 +00:00
John
3f34e17bd4 Add new file 2021-06-03 08:14:29 +00:00
tool4EvEr
196b48cbd3 Reduce FailedToTarget scenario 2021-06-03 10:14:16 +02:00
John
01bfa59c6a Update sythis_harvests_hand.txt 2021-06-03 07:51:46 +00:00
John
a921ef1ec7 Update goblin_traprunner.txt 2021-06-03 07:51:19 +00:00
John
d30f2b9d16 Update hard_evidence.txt 2021-06-03 07:50:58 +00:00
John
8f887e5242 Update goblin_traprunner.txt 2021-06-03 07:50:39 +00:00
John
f742a65af8 Update captain_ripley_vance.txt 2021-06-03 07:50:17 +00:00
John
868ba6d232 Update floodhound.txt 2021-06-03 07:49:58 +00:00
paul_snoops
47384bec0e MH2 and RMH1 updates 2021-06-03 08:29:26 +01:00
Michael Kamensky
2144de87d4 Merge branch 'velus' into 'master'
MH2: Mount Velus Manticore and support

See merge request core-developers/forge!4809
2021-06-03 05:15:59 +00:00
Michael Kamensky
7a3c9c5b49 Merge branch 'sojourn' into 'master'
MH2: Sojourner's Companion w/ updates to TypeCycling to parse "Artifact.Land"

See merge request core-developers/forge!4810
2021-06-03 05:15:34 +00:00
Michael Kamensky
df35468771 Merge branch 'mh2_2' into 'master'
MH2 - 2 June

See merge request core-developers/forge!4805
2021-06-03 05:14:59 +00:00
Northmoc
5ac9d3fdff AbilityUtils support for Count$CardNumTypes / Count$RememberedCardNumTypes 2021-06-02 15:52:41 -04:00
Northmoc
de73ed1986 mount_velus_manticore.txt 2021-06-02 15:51:13 -04:00
Northmoc
da2a92b74a CardFactoryUtil prep TypeCycling for Artifact landcycling 2021-06-02 15:06:29 -04:00
Northmoc
9aba2db8ce KeywordWithCostAndType.java set artifact landcycling for reminder text 2021-06-02 15:05:30 -04:00
Northmoc
5141be475b sojourners_companion.txt 2021-06-02 15:04:38 -04:00
John
9f8e7616d1 Delete chitterspitter.txt 2021-06-02 17:03:13 +00:00
John
f7c7cdef8c Delete nykthos_paragon.txt 2021-06-02 17:02:59 +00:00
John
98e529304f Add new file 2021-06-02 16:11:23 +00:00
John
c04290493e Add new file 2021-06-02 16:10:28 +00:00
John
12695faba2 Add new file 2021-06-02 16:09:55 +00:00
John
c1a064e40a Add new file 2021-06-02 16:09:06 +00:00
John
7798594071 Add new file 2021-06-02 16:04:16 +00:00
John
34281c2850 Add new file 2021-06-02 16:03:29 +00:00
John
17365a6e2c Add new file 2021-06-02 16:02:40 +00:00
John
9b150e3263 Add new file 2021-06-02 16:01:56 +00:00
John
a1648c70c6 Add new file 2021-06-02 16:01:11 +00:00
John
1a0aa37992 Add new file 2021-06-02 15:59:28 +00:00
John
ad4772ba01 Add new file 2021-06-02 15:55:32 +00:00
John
78b6e16eb3 Add new file 2021-06-02 15:54:59 +00:00
John
682c61d025 Add new file 2021-06-02 15:54:29 +00:00
Northmoc
b026479f78 gaeas_will.txt tidy 2021-06-02 11:54:07 -04:00
John
1d6fbba6d2 Add new file 2021-06-02 15:53:41 +00:00
John
142a47346d Add new file 2021-06-02 15:51:36 +00:00
John
406d1023e8 Add new file 2021-06-02 15:49:57 +00:00
John
895db0526b Add new file 2021-06-02 15:49:16 +00:00
John
d2ecf48af9 Add new file 2021-06-02 15:48:20 +00:00
John
edca8bec50 Add new file 2021-06-02 15:47:16 +00:00
John
b99571c28f Add new file 2021-06-02 15:46:43 +00:00
John
70d9d9273b Add new file 2021-06-02 15:45:59 +00:00
John
bcd0b0cd46 Add new file 2021-06-02 15:44:37 +00:00
John
49238a1283 Add new file 2021-06-02 15:43:32 +00:00
John
7a11129a5e Add new file 2021-06-02 15:41:56 +00:00
Northmoc
395fc13142 blossoming_calm.txt (medusa) 2021-06-02 11:35:14 -04:00
tool4EvEr
3b594d239c Update script 2021-06-02 17:30:31 +02:00
Northmoc
e7701ee0f9 rw_4_4_golem.txt for General Ferrous 2021-06-02 11:27:46 -04:00
Bug Hunter
80e8f5db8a Update goblin_artisans.txt 2021-06-02 06:51:46 +00:00
Michael Kamensky
608eea8e7d Merge branch 'mh2_1' into 'master'
MH2 - 1 June

See merge request core-developers/forge!4804
2021-06-02 04:42:00 +00:00
Michael Kamensky
7dae92b042 Merge branch 'fixing' into 'master'
Card fixes

Closes #1866

See merge request core-developers/forge!4803
2021-06-02 04:41:15 +00:00
Northmoc
62bc67cbe1 blacksmiths_skill.txt (Suthro) 2021-06-01 19:17:55 -04:00
Northmoc
fbcbf0afe5 barbed_spike.txt (Suthro) 2021-06-01 19:13:50 -04:00
Northmoc
6b06873568 arcbound_javelineer.txt (medusa) 2021-06-01 19:10:18 -04:00
Northmoc
2007a8d05c kaleidoscorch.txt 2021-06-01 18:53:51 -04:00
Northmoc
1007772ef3 funnel_web_recluse.txt 2021-06-01 18:53:19 -04:00
Northmoc
23785ea8b6 add AI hint 2021-06-01 18:52:58 -04:00
Northmoc
10a6b43a97 tavern_scoundrel.txt 2021-06-01 18:39:12 -04:00
Northmoc
72e1d8177c ornithopter_of_paradise.txt 2021-06-01 18:38:53 -04:00
tool4EvEr
6ab9bfa0fa Add pump 2021-06-01 22:33:49 +02:00
tool4EvEr
faeb73d8b7 Clean up 2021-06-01 22:29:15 +02:00
tool4EvEr
beff3d4832 Fix Runaway Carriage 2021-06-01 22:11:01 +02:00
tool4EvEr
29a4fdb47a prevent StackDescription from revealing face 2021-06-01 21:47:58 +02:00
tool4EvEr
931411b6e4 Fix Wandering Archaic 2021-06-01 18:38:26 +02:00
tool4EvEr
8157172a9f Fix City in a Bottle 2021-06-01 17:53:53 +02:00
Michael Kamensky
5ac73d9f90 Merge branch 'fix-deck-editor-custom-editions-image-view' into 'master'
Improvements to Deck Editor for Custom Editions

See merge request core-developers/forge!4796
2021-06-01 04:26:05 +00:00
Michael Kamensky
27085243c4 Merge branch 'historic-formats' into 'master'
Update the "historic" formats

See merge request core-developers/forge!4802
2021-06-01 04:25:39 +00:00
Michael Kamensky
16b2fd93d8 Merge branch 'spellcast' into 'master'
Fix missing intrinsic Cast trigger from copied host

See merge request core-developers/forge!4754
2021-06-01 04:25:28 +00:00
Bug Hunter
edce6ed9e8 Fix missing intrinsic Cast trigger from copied host 2021-06-01 04:25:27 +00:00
Rob Schnautz
e9b6db6994 erroneous date/name 2021-05-31 20:30:24 +00:00
Rob Schnautz
f5a1671e86 Catching up old formats 2021-05-31 20:04:04 +00:00
leriomaggio
560d8ec5c0 Refactored duplicate code in StaticData w/ new getCardEdition method
StaticData now includes a new method, namely `getCardEdition` which looks for a CardEdition instance into editions and customeditions given an input set code.

This new method has been refactored and used throughout the new changes in Advanced Search and Filters.
2021-05-31 19:57:33 +01:00
leriomaggio
4e7a3fe515 Updated Chinese translation after CCTV-1 suggestions in MR comments 2021-05-31 19:30:43 +01:00
leriomaggio
1ac28b5203 Merge remote-tracking branch 'upstream/master' into fix-deck-editor-custom-editions-image-view 2021-05-31 19:25:20 +01:00
Michael Kamensky
9b69d79a78 Merge branch 'Williams-master-patch-60436' into 'master'
MH2 Contributions 30/05

See merge request core-developers/forge!4801
2021-05-31 16:53:19 +00:00
John
9e78a4ef99 Update tireless_provisioner.txt 2021-05-31 13:17:30 +00:00
John
8d2ef41d54 Update tide_shaper.txt 2021-05-31 13:17:03 +00:00
John
c507e960fd Update thought_monitor.txt 2021-05-31 13:16:05 +00:00
John
490f700e37 Update skyblades_boon.txt 2021-05-31 13:15:47 +00:00
John
355f379f21 Update combine_chrysalis.txt 2021-05-31 13:13:34 +00:00
Michael Kamensky
e69f8a2e7a Merge branch 'fixes' into 'master'
Some card fixes

Closes #1860

See merge request core-developers/forge!4798
2021-05-31 11:10:12 +00:00
Bug Hunter
450d5b6144 Some card fixes 2021-05-31 11:10:11 +00:00
John
41fdc485b4 Add new file 2021-05-31 10:33:54 +00:00
John
4d25aee6dc Add new file 2021-05-31 10:33:16 +00:00
John
46574f8137 Add new file 2021-05-31 10:32:35 +00:00
John
71cbd1221b Add new file 2021-05-31 10:31:22 +00:00
John
7386a9b614 Add new file 2021-05-31 10:30:50 +00:00
John
d5cfc39744 Add new file 2021-05-31 10:29:35 +00:00
leriomaggio
8a1770ffda FIX Editions in Checkboxlist sorted (latest to earliest) 2021-05-31 11:19:08 +01:00
leriomaggio
01e51ecb73 Custom Editions are now included in Advanced Search Filters 2021-05-31 10:45:49 +01:00
leriomaggio
43a62813e6 Custom Editions are now included in GameFormat when checking for sets 2021-05-31 10:44:48 +01:00
leriomaggio
1b25438fca New Extra Panel with Checkbox list for Custom Edition
Besides: since Custom Editions can be sometimes not that many, the number of displayed rows in Checkbox lists has been fixed to 20 to have all panels of same height.
2021-05-31 10:39:31 +01:00
leriomaggio
5858de4860 ColumnDef now includes Custom Editionsi in sorting 2021-05-31 10:36:11 +01:00
leriomaggio
8128a3374b New Label for Other and Custom Sets for Advanced Filter Panel 2021-05-31 10:35:23 +01:00
leriomaggio
32cc860eda Changed getSortedEditions to now also include Custom Sets 2021-05-31 10:18:21 +01:00
leriomaggio
03d6687c64 Merge remote-tracking branch 'upstream/master' into fix-deck-editor-custom-editions-image-view 2021-05-31 09:12:32 +01:00
Michael Kamensky
51924d16db Merge branch 'scrolling' into 'master'
Workshop: Add scrollbars

Closes #6

See merge request core-developers/forge!4800
2021-05-31 06:43:25 +00:00
Michael Kamensky
cd167313b7 Merge branch 'Williams-master-patch-52159' into 'master'
MH2 Contributions 29/05

See merge request core-developers/forge!4793
2021-05-31 06:43:12 +00:00
John
365142c860 MH2 Contributions 29/05 2021-05-31 06:43:11 +00:00
Michael Kamensky
990f6f148a Merge branch 'refactor_replace_damage_buffered' into 'master'
Refactor replace damage to buffer SA execution

See merge request core-developers/forge!4797
2021-05-31 06:41:17 +00:00
Alumi
1eb6f33207 Refactor replace damage to handle all damage related replacments first, then run all buffered SAs after 2021-05-31 06:41:16 +00:00
tool4EvEr
2d23a5b37d Add scrollbars 2021-05-30 22:26:35 +02:00
tool4EvEr
65f400cf46 Fix NPE 2021-05-30 18:34:44 +02:00
Michael Kamensky
84dfdfc11c Merge branch 'mh2_29' into 'master'
MH2 - 29 May

See merge request core-developers/forge!4795
2021-05-30 05:36:47 +00:00
Michael Kamensky
ecb1cb8a0e Merge branch 'cleaning' into 'master'
Fix mana not replaced when generated for different player

See merge request core-developers/forge!4794
2021-05-30 05:36:23 +00:00
Michael Kamensky
b9a8da1231 Merge branch 'junk' into 'master'
Junk Winder - Affinity detail/reminder text updates for more complex affinities

See merge request core-developers/forge!4787
2021-05-30 05:36:19 +00:00
Michael Kamensky
27c9bee138 Merge branch 'patch-sortable-collector-number-non-numerical' into 'master'
FIX non-numerical CollectorNumber as Sorting Key

See merge request core-developers/forge!4789
2021-05-30 05:36:09 +00:00
leriomaggio
9902718082 FIX and Impros to CardEdition, CardInSet and Reader
- Reader has a new updated regexp to deal with non-numerical collectorNumbers
- CardInSet have been now made sortable based on CollectorNumber.
To do so, collectorNumbers are transformed accordingly to allow for natural ordering (as expected) instead of lexicographic order.
- CardEdition now return cards (CardInSet) as sorted, to allow for correct artIndex matching when creating corresponding `PaperCard` instances.
Moreover, `compareTo` of card edition has been improved to also take into account set name (in cases of same release date).
2021-05-30 05:36:09 +00:00
Michael Kamensky
270ab5e843 Merge branch 'untapp' into 'master'
avoid pumping opponent creature

Closes #1134

See merge request core-developers/forge!4792
2021-05-30 05:23:56 +00:00
leriomaggio
62961889d5 Fixed typo in Label Name: RepRints (r missing) 2021-05-30 02:05:09 +01:00
leriomaggio
0217c4b4b2 Support collectorNumber for Cards in Custom sets
retrieveCollectorNumber now also considers customEditions when looking for the matching collectorNumber of a PaperCard instance.
2021-05-30 00:21:05 +01:00
leriomaggio
1c950a8a57 ImageView now includes also CustomEditions in Sets Map
This fix solves an issue with Custom Editions appearing as "Other" in ImageView. The bug was due to customEditions not being included in sets map.
2021-05-29 23:43:47 +01:00
leriomaggio
f5563ccedb New method to return the Collection of Custom Editions 2021-05-29 23:42:34 +01:00
Northmoc
0672477817 moderation.txt 2021-05-29 17:08:29 -04:00
Northmoc
c28a1f740e nettlecyst.txt 2021-05-29 16:59:12 -04:00
Northmoc
e0f3b65251 strike_it_rich.txt 2021-05-29 16:57:16 -04:00
Northmoc
001acda866 fast_furious.txt yank Cost 2021-05-29 16:57:01 -04:00
Northmoc
eb9ceb4dc8 fast_furious.txt 2021-05-29 16:48:42 -04:00
tool4EvEr
d622f9075a Add ValidActivator 2021-05-29 20:59:49 +02:00
Northmoc
76699ad323 batterbone.txt 2021-05-29 11:14:43 -04:00
tool4EvEr
a5fc5c1e40 Fix mana not replaced when generated for different player 2021-05-29 17:06:08 +02:00
Tim Mocny
cb9302b2fe Merge branch 'svyelun' into 'master'
Svyelun name fix

See merge request core-developers/forge!4791
2021-05-29 15:04:32 +00:00
Northmoc
254ddf8dfb KeywordWithType.java special parsing for Affinity 2021-05-29 11:00:36 -04:00
Northmoc
4995cc8876 CardFactoryUtil.java implement optional additional param for complex Affinities 2021-05-29 10:59:15 -04:00
Northmoc
1f120a7faf Affinity reminder text remove extra "you" 2021-05-29 10:58:42 -04:00
Northmoc
4411910930 junk_winder.txt fix and add to keyword 2021-05-29 10:53:41 -04:00
tool4EvEr
931dc04ad2 avoid pumping opponent creature 2021-05-29 12:01:10 +02:00
paul_snoops
e6b390addc Svyelun name fix 2021-05-29 10:55:46 +01:00
paul_snoops
a4027c0ad8 Svyelun name fix 2021-05-29 10:47:52 +01:00
Bug Hunter
3451fbf42a Merge branch 'rename' into 'master'
Fix card filename

See merge request core-developers/forge!4790
2021-05-29 09:01:44 +00:00
tool4EvEr
65e3c24de6 Fix card filename 2021-05-29 11:01:45 +02:00
Michael Kamensky
90e74c1c3a Merge branch 'Williams-master-patch-93999' into 'master'
MH2 Contributions 28/05

See merge request core-developers/forge!4781
2021-05-29 06:54:18 +00:00
Michael Kamensky
4610729a82 Merge branch 'patch-imagefetcher-scryfall' into 'master'
FIX ImageFetcher against Scryfall, so that download can be robust (against different set-codes) and reliable.

See merge request core-developers/forge!4788
2021-05-29 06:54:06 +00:00
Michael Kamensky
ab8c2c8b10 Merge branch 'mh2_28' into 'master'
MH2 - 28 May

See merge request core-developers/forge!4786
2021-05-29 06:53:50 +00:00
Michael Kamensky
bf0a5e13ae Merge branch 'fix' into 'master'
Fixes!

See merge request core-developers/forge!4785
2021-05-29 06:52:24 +00:00
Northmoc
e4f8f7852f naya_soulbeast.txt fix 2021-05-28 21:47:27 -04:00
leriomaggio
348365b8fd Merge remote-tracking branch 'upstream/master' into patch-imagefetcher-scryfall 2021-05-28 23:30:06 +01:00
leriomaggio
5c2f62defa FIX Scryfall Download retrieval, that is robust with different set-codes.
Imagefetcher now tries MCI code first and if that's not working, tries other set codes until one matching scryfall will be found. If none, the not-working set will be recorded to avoid future computation, and returns.
2021-05-28 23:29:29 +01:00
Northmoc
bd5a98fe13 academy_manufactor.txt 2021-05-28 17:01:44 -04:00
Northmoc
8a0da67705 crawling_barrens.txt tidy up StackDesc 2021-05-28 17:01:14 -04:00
Northmoc
479cbbf671 suspend.txt tighten up, add AI 2021-05-28 17:00:57 -04:00
Northmoc
6708ca00b5 suspend.txt fixup 2021-05-28 16:59:37 -04:00
John
53e08bbf55 Update verdant_command.txt 2021-05-28 20:25:53 +00:00
John
d389050a66 Update underworld_hermit.txt 2021-05-28 20:25:36 +00:00
John
5d4f628b8b Update tourach_dread_cantor.txt 2021-05-28 20:25:19 +00:00
John
2ecbd26477 Update sweep_the_skies.txt 2021-05-28 20:25:01 +00:00
John
427e0d895f Update svyelun_of_sea_and_sky.txt 2021-05-28 20:24:42 +00:00
John
dfb86d40a5 Update squirrel_mob.txt 2021-05-28 20:24:20 +00:00
John
b08c7cc9e6 Update scuttletide.txt 2021-05-28 20:24:01 +00:00
John
2bbe9464b8 Update necromancers_familiar.txt 2021-05-28 20:23:43 +00:00
John
ef5937d5ee Update grist_the_hunger_tide.txt 2021-05-28 20:23:24 +00:00
John
289846a53a Update grist_the_hunger_tide.txt 2021-05-28 20:22:43 +00:00
John
6bc361bd0c Update feast_of_sanity.txt 2021-05-28 20:22:15 +00:00
John
47274b0085 Update breyas_apprentice.txt 2021-05-28 20:21:41 +00:00
Northmoc
d3ebd83c32 angelic_arbiter.txt correct param 2021-05-28 14:40:34 -04:00
leriomaggio
b076a57811 New utility method to compose Scryfall Download url that is compliant with new API specs 2021-05-28 19:19:08 +01:00
Tim Mocny
e9285ad46d Merge branch 'editions' into 'master'
MH2 & RMH1 edition updates

See merge request core-developers/forge!4784
2021-05-28 17:55:46 +00:00
Paul Hammerton
5d91a1159e MH2 & RMH1 edition updates 2021-05-28 17:55:46 +00:00
John
ee65c22a40 Add new file 2021-05-28 16:08:31 +00:00
John
1cb5c9a711 Add new file 2021-05-28 16:08:01 +00:00
John
f22a3e081d Add new file 2021-05-28 16:07:19 +00:00
John
fff6a529e5 Add new file 2021-05-28 16:06:37 +00:00
John
2260957b00 Add new file 2021-05-28 16:05:55 +00:00
Michael Kamensky
42c845bb72 Merge branch 'collector-number-in-card-list-and-card-db-refactoring' into 'master'
Improvement to Performance to CardEdition, and FIX bug for non-existing cards.

See merge request core-developers/forge!4783
2021-05-28 15:37:45 +00:00
leriomaggio
8b7ae19508 FIX and Impros to CardEdition, CardInSet and Reader
- Reader has a new updated regexp to deal with non-numerical collectorNumbers
- CardInSet have been now made sortable based on CollectorNumber.
To do so, collectorNumbers are transformed accordingly to allow for natural ordering (as expected) instead of lexicographic order.
- CardEdition now return cards (CardInSet) as sorted, to allow for correct artIndex matching when creating corresponding `PaperCard` instances.
Moreover, `compareTo` of card edition has been improved to also take into account set name (in cases of same release date).
2021-05-28 15:37:44 +00:00
Michael Kamensky
c13274d0a7 Merge branch 'editions' into 'master'
AFR, MH2 & RMH1 edition updates

See merge request core-developers/forge!4778
2021-05-28 15:36:18 +00:00
Michael Kamensky
2a424ca3a9 Merge branch 'wave' into 'master'
Fix Parallax Wave

Closes #1853

See merge request core-developers/forge!4774
2021-05-28 15:35:21 +00:00
Bug Hunter
8effb2cb76 Fix Parallax Wave 2021-05-28 15:35:21 +00:00
Michael Kamensky
a03ce0e82c Merge branch 'fix_mutated_flip' into 'master'
Fix bug that can't flip or transform when mutated

See merge request core-developers/forge!4782
2021-05-28 15:34:27 +00:00
John
c0913a1b05 Add new file 2021-05-28 15:28:06 +00:00
John
f504581468 Add new file 2021-05-28 15:27:31 +00:00
John
f4528151ac Add new file 2021-05-28 15:26:24 +00:00
John
2475ea9ec9 Add new file 2021-05-28 15:25:47 +00:00
John
cc380a99f1 Add new file 2021-05-28 15:22:02 +00:00
John
7c34720cf7 Add new file 2021-05-28 15:21:17 +00:00
John
6debbe6853 Add new file 2021-05-28 15:18:18 +00:00
paul_snoops
d9cf784498 AFR, MH2 & RMH1 edition updates 2021-05-28 15:47:14 +01:00
John
12b8ec740b Add new file 2021-05-28 14:22:43 +00:00
paul_snoops
ff6d492857 AFR, MH2 & RMH1 edition updates 2021-05-28 15:11:21 +01:00
paul_snoops
4ea29526b1 AFR, MH2 & RMH1 edition updates 2021-05-28 14:16:06 +01:00
paul_snoops
aae3772a3a AFR, MH2 & RMH1 edition updates 2021-05-28 14:05:30 +01:00
paul_snoops
3639ced11f AFR, MH2 & RMH1 edition updates 2021-05-28 13:43:17 +01:00
John
a5dfcd4eb6 Add new file 2021-05-28 12:33:05 +00:00
John
204191973e Add new file 2021-05-28 12:32:01 +00:00
John
39b55f9045 Add new file 2021-05-28 12:30:39 +00:00
John
f02bb9d4ba Add new file 2021-05-28 12:29:47 +00:00
John
8b897c8a55 Add new file 2021-05-28 12:29:23 +00:00
John
f25bc35ee3 Add new file 2021-05-28 12:28:27 +00:00
John
81a41fb941 Update TypeLists.txt 2021-05-28 12:23:09 +00:00
John
c714ac2175 Add new file 2021-05-28 12:21:51 +00:00
John
f131bcb0d9 Add new file 2021-05-28 12:19:40 +00:00
John
07627b9320 Add new file 2021-05-28 12:18:56 +00:00
John
f32dab48a4 Add new file 2021-05-28 12:18:32 +00:00
John
61abb8cf0d Add new file 2021-05-28 12:17:47 +00:00
Lyu Zong-Hong
bbbee3421d Fix bug that can't flip or transform when mutated 2021-05-28 21:17:24 +09:00
John
972a79309b Add new file 2021-05-28 12:17:00 +00:00
John
d318606102 Add new file 2021-05-28 12:16:34 +00:00
John
8b057ea199 Add new file 2021-05-28 12:16:02 +00:00
John
edaa9f885a Add new file 2021-05-28 12:15:31 +00:00
paul_snoops
616760cbbb MH2 & RMH1 edition updates 2021-05-28 13:14:44 +01:00
paul_snoops
6a120f5839 MH2 & RMH1 edition updates 2021-05-28 13:11:35 +01:00
John
3fa7546ba6 Add new file 2021-05-28 11:34:59 +00:00
John
b7fd4d094c Add new file 2021-05-28 11:34:35 +00:00
John
fb92e06d62 Add new file 2021-05-28 11:34:08 +00:00
John
744573817a Add new file 2021-05-28 11:33:40 +00:00
John
b84dc7b721 Add new file 2021-05-28 11:33:10 +00:00
John
e88191c54a Add new file 2021-05-28 11:32:40 +00:00
John
a32bc8c378 Add new file 2021-05-28 11:32:12 +00:00
John
cb5be7cec7 Add new file 2021-05-28 11:31:40 +00:00
John
209eb9c397 Add new file 2021-05-28 11:31:10 +00:00
John
58b6b1cd0c Add new file 2021-05-28 11:30:05 +00:00
paul_snoops
b8c27ec2a9 MH2 & RMH1 edition updates 2021-05-28 12:20:44 +01:00
Anthony Calosa
a17c42e07e Merge branch 'kevlahnota-master-patch-91638' into 'master'
unused import

See merge request core-developers/forge!4780
2021-05-28 10:20:32 +00:00
Anthony Calosa
cb9a2d7b8b unused import 2021-05-28 10:20:13 +00:00
Anthony Calosa
a7b215fa19 Merge branch 'kevlahnota-master-patch-18523' into 'master'
Update FileUtil.java

See merge request core-developers/forge!4779
2021-05-28 10:12:15 +00:00
Anthony Calosa
367170d33b Update FileUtil.java 2021-05-28 10:10:54 +00:00
paul_snoops
695cb1496d MH2 & RMH1 edition updates 2021-05-28 10:20:20 +01:00
Michael Kamensky
741ca2646e Merge branch 'read_file_with_utf8' into 'master'
Update FileUtil.readFile to use UTF-8 charset to read files.

See merge request core-developers/forge!4777
2021-05-28 08:59:54 +00:00
Michael Kamensky
2ac4b923fe Merge branch 'vectis' into 'master'
MH2: Vectis Gloves and support

See merge request core-developers/forge!4772
2021-05-28 08:59:01 +00:00
Michael Kamensky
cac15b1436 Merge branch 'taste' into 'master'
MH2: Discerning Taste and Support

See merge request core-developers/forge!4770
2021-05-28 08:57:41 +00:00
Michael Kamensky
e77281cfb6 Merge branch 'patch-content-downloader-macos' into 'master'
FIX annoying bug increasing display brightness on macOS when starting content downloader

See merge request core-developers/forge!4768
2021-05-28 08:57:09 +00:00
Lyu Zong-Hong
81d0a71060 Update FileUtil.readFile to use UTF-8 charset to read files. 2021-05-28 17:25:12 +09:00
leriomaggio
66cb1c28f7 Switched to F1 - should be less annoying than F2 2021-05-28 07:05:06 +01:00
Michael Kamensky
051e3eb922 Merge branch 'mh2_27' into 'master'
MH2 - 27 May

See merge request core-developers/forge!4776
2021-05-28 03:39:34 +00:00
Michael Kamensky
bd93dd543e Merge branch 'drainlife' into 'master'
Fix Drain Life

Closes #1854

See merge request core-developers/forge!4775
2021-05-28 03:38:26 +00:00
Michael Kamensky
1382ef1724 Merge branch 'Williams-master-patch-31829' into 'master'
MH2 Contributions 27/05

See merge request core-developers/forge!4773
2021-05-28 03:38:07 +00:00
Michael Kamensky
6004850792 Merge branch 'fix' into 'master'
Various card fixes

See merge request core-developers/forge!4769
2021-05-28 03:36:15 +00:00
Northmoc
79d43f76b3 harmonic_prodigy.txt 2021-05-27 20:47:11 -04:00
Northmoc
d387287a04 dermotaxi.txt 2021-05-27 20:36:07 -04:00
Northmoc
413e209b0b out_of_time.txt 2021-05-27 19:50:08 -04:00
Northmoc
1124069e52 calibrated_blast.txt 2021-05-27 18:54:47 -04:00
tool4EvEr
3d0009f23e Fix Drain Life 2021-05-27 23:32:33 +02:00
John
4df0cf1049 Add new file 2021-05-27 21:03:44 +00:00
John
eb91e10661 Add new file 2021-05-27 21:03:23 +00:00
John
88dbfba282 Add new file 2021-05-27 21:02:43 +00:00
John
f6358f10f2 Add new file 2021-05-27 21:02:03 +00:00
John
51b5c81aac Update so_shiny.txt 2021-05-27 21:00:37 +00:00
Northmoc
20ff40896f infuse_with_vitality.txt fix 2021-05-27 16:47:46 -04:00
John
df78d375fe Update junk_winder.txt 2021-05-27 19:55:53 +00:00
John
206194353c Update arcbound_mouser.txt 2021-05-27 19:55:23 +00:00
John
dcd16704bd Update dress_down.txt 2021-05-27 19:54:26 +00:00
John
2b6b2a5e95 Update arcbound_mouser.txt 2021-05-27 19:53:45 +00:00
John
c767a3b3ad Add new file 2021-05-27 17:11:10 +00:00
John
06a4eac1c9 Add new file 2021-05-27 17:10:27 +00:00
John
ffba3ef18a Add new file 2021-05-27 17:09:41 +00:00
John
1a6d6e1290 Add new file 2021-05-27 17:09:03 +00:00
John
e75dedcffa Add new file 2021-05-27 16:36:16 +00:00
John
a6e50452f0 Add new file 2021-05-27 16:35:40 +00:00
John
008dd2f1d7 Add new file 2021-05-27 16:34:14 +00:00
John
82c3f32a5e Add new file 2021-05-27 16:33:41 +00:00
John
5c0a03e242 Add new file 2021-05-27 16:32:12 +00:00
John
e25ce911ed Add new file 2021-05-27 16:31:46 +00:00
John
b032002c33 Add new file 2021-05-27 16:29:27 +00:00
Northmoc
667bd138c8 bridge cycle (Suthro) 2021-05-27 11:36:07 -04:00
Northmoc
f0f017713e add Artifact landwalk to CombatUtil 2021-05-27 11:13:01 -04:00
Northmoc
331ae6ff44 vectis_gloves.txt 2021-05-27 11:12:27 -04:00
Tim Mocny
ee05f34963 Merge branch 'editions' into 'master'
MH2 & RMH1 edition updates

See merge request core-developers/forge!4771
2021-05-27 14:17:24 +00:00
paul_snoops
2f6aa3dc0f MH2 & RMH1 edition updates 2021-05-27 14:49:29 +01:00
paul_snoops
c9c5ae0058 MH2 & RMH1 edition updates 2021-05-27 14:43:39 +01:00
Northmoc
bff5a13c25 add support for "RememberMovedToZone" to DigEffect 2021-05-27 09:08:52 -04:00
Northmoc
68781783c4 discerning_taste.txt 2021-05-27 09:07:11 -04:00
Northmoc
04ba6bdd7b add Ultimate tag for PW achievements 2021-05-27 08:30:47 -04:00
Michael Kamensky
f557420c7a Merge branch 'collector-number-in-card-list-and-card-db-refactoring' into 'master'
FIX and Improvements to Card Edition Sorting, Cards Processing from Oracle, and Cards Art Index Matching

See merge request core-developers/forge!4756
2021-05-27 10:38:51 +00:00
leriomaggio
8d2d633f6d FIX and Impros to CardEdition, CardInSet and Reader
- Reader has a new updated regexp to deal with non-numerical collectorNumbers
- CardInSet have been now made sortable based on CollectorNumber.
To do so, collectorNumbers are transformed accordingly to allow for natural ordering (as expected) instead of lexicographic order.
- CardEdition now return cards (CardInSet) as sorted, to allow for correct artIndex matching when creating corresponding `PaperCard` instances.
Moreover, `compareTo` of card edition has been improved to also take into account set name (in cases of same release date).
2021-05-27 10:38:51 +00:00
leriomaggio
39ed24d133 FIX annoying bug increasing display brightness on macOS when starting content downloader 2021-05-27 11:26:28 +01:00
Anthony Calosa
94ec700c78 Merge branch 'kevlahnota-master-patch-24830' into 'master'
Update ChangeTargetsEffect.java

See merge request core-developers/forge!4767
2021-05-27 08:55:13 +00:00
Anthony Calosa
92ac19fa2e Merge branch 'kevlahnota-master-patch-27790' into 'master'
Update CopySpellAbilityEffect.java

See merge request core-developers/forge!4766
2021-05-27 08:55:04 +00:00
Anthony Calosa
c96aeb25e5 Update ChangeTargetsEffect.java 2021-05-27 08:48:52 +00:00
Anthony Calosa
63ebd462b9 Update CopySpellAbilityEffect.java 2021-05-27 08:48:11 +00:00
Michael Kamensky
68fe024d5a Merge branch 'drawai' into 'master'
Fix drawing negative amounts

See merge request core-developers/forge!4761
2021-05-27 04:25:49 +00:00
Michael Kamensky
12c0207f22 Merge branch 'chefs_kiss' into 'master'
MH2: Chef's Kiss and support

See merge request core-developers/forge!4765
2021-05-27 04:25:33 +00:00
Michael Kamensky
402eade4c2 Merge branch 'fix' into 'master'
binding_the_old_gods.txt fix

See merge request core-developers/forge!4762
2021-05-27 04:25:07 +00:00
Northmoc
c3f88773d2 support for RandomTargetRestriction on ChangeTargetsEffect 2021-05-26 23:36:29 -04:00
Northmoc
5c689ebf9b support for RandomTarget and RandomTargetRestriction on CopySpellAbilityEffect 2021-05-26 23:35:47 -04:00
Northmoc
efa99cb9ac chefs_kiss.txt 2021-05-26 23:33:44 -04:00
swordshine
19a96aac00 Merge branch 'suicide' into 'master'
Wheel of Misfortune: Fix suicide

See merge request core-developers/forge!4755
2021-05-27 01:01:03 +00:00
swordshine
6c84d433f6 Merge branch 'young' into 'master'
MH2: Young Necromancer

See merge request core-developers/forge!4759
2021-05-27 01:00:30 +00:00
swordshine
63c4ab822c Merge branch 'mh2_26' into 'master'
MH2 - 26 May

See merge request core-developers/forge!4763
2021-05-27 00:55:37 +00:00
swordshine
f343397a68 Merge branch 'ezellohar-master-patch-51670' into 'master'
Fixed a few typos and other minor improvements

See merge request core-developers/forge!4764
2021-05-27 00:51:39 +00:00
ezellohar
3993f5a715 Fixed a few typos and other minor improvements 2021-05-26 21:33:15 +00:00
Northmoc
ac66b6ac1b young_necromancer.txt "When you do" 2021-05-26 16:04:56 -04:00
Northmoc
de5c07d74b the_underworld_cookbook.txt 2021-05-26 15:53:12 -04:00
Northmoc
4f8a9b4d3f asmoranomardicadaistinaculdacar.txt 2021-05-26 15:52:57 -04:00
Northmoc
1e643b3a99 binding_the_old_gods.txt fix 2021-05-26 15:45:10 -04:00
tool4EvEr
eddbe33f2e Fix drawing negative amounts 2021-05-26 18:19:07 +02:00
Tim Mocny
6b07e07bea Merge branch 'edition_updates' into 'master'
New edition file updates

See merge request core-developers/forge!4757
2021-05-26 13:36:21 +00:00
Northmoc
8ff8502fed young_necromancer.txt 2021-05-26 09:32:46 -04:00
paul_snoops
31ee93a2a0 AFR, RMH1 & MH2 - New cards added
HA5 - Whole set added
SLD - Jumpstart foil lands added

RMH1 & MH2: The occasional collector number may be incorrect but will finalise when whole set is spolied
2021-05-26 14:27:47 +01:00
tool4EvEr
ce17685154 Fix suicide 2021-05-26 12:56:16 +02:00
Bug Hunter
4927e6135d Merge branch 'collector-number-in-card-list-and-card-db-refactoring' into 'master'
Restore Missing entries in Card Edition Resource files

See merge request core-developers/forge!4749
2021-05-26 08:57:46 +00:00
leriomaggio
6725873c8c Restore Missing entries in Card Edition Resource files 2021-05-26 08:57:45 +00:00
Bug Hunter
90b1a244a5 Merge branch 'fix_flowstone_shambler' into 'master'
Fix attack pump for Flowstone Shambler and Gateway Shade

See merge request core-developers/forge!4753
2021-05-26 07:30:55 +00:00
Lyu Zong-Hong
c3a7191f31 Fix attack pump for Flowstone Shambler and Gateway Shade 2021-05-26 14:00:24 +09:00
Michael Kamensky
e3a2a98c2f Merge branch 'mimicvat' into 'master'
Mimic Vat: Update trigger so it doesn't resolve when card moved

See merge request core-developers/forge!4741
2021-05-26 04:34:55 +00:00
Michael Kamensky
35aaafeba6 Merge branch 'fires' into 'master'
Fix Fires of Invention

See merge request core-developers/forge!4736
2021-05-26 04:34:29 +00:00
Michael Kamensky
0c9d3c9152 Merge branch 'update-nassari' into 'master'
Update nassari

See merge request core-developers/forge!4752
2021-05-26 04:33:25 +00:00
Michael Kamensky
6a93608427 Merge branch 'yusri' into 'master'
MH2: Yusri and support

See merge request core-developers/forge!4750
2021-05-26 04:32:52 +00:00
Michael Kamensky
c2b84ce240 Merge branch 'mh2_25' into 'master'
MH2 - 25 May

See merge request core-developers/forge!4748
2021-05-26 04:32:21 +00:00
Michael Kamensky
e6b876314c Merge branch 'void' into 'master'
MH2: Void Mirror and support

See merge request core-developers/forge!4747
2021-05-26 04:31:48 +00:00
Michael Kamensky
9cb73fe9fc Merge branch 'mh2_24' into 'master'
MH2 - 24 May

See merge request core-developers/forge!4746
2021-05-26 04:30:44 +00:00
Michael Kamensky
f4f0ae990f Merge branch 'Williams-master-patch-29065' into 'master'
MH2/AFR Contributions

See merge request core-developers/forge!4740
2021-05-26 04:30:21 +00:00
Michael Kamensky
50c314af7d Merge branch 'ezellohar-master-patch-05605' into 'master'
Update it-IT.properties

See merge request core-developers/forge!4751
2021-05-26 04:30:04 +00:00
friarsol
bd18a5bb41 Update nassari 2021-05-25 23:38:30 -04:00
tool4EvEr
263faa9895 Clean up 2021-05-25 22:29:49 +02:00
Filippo Vomiero
36cc45b227 Update it-IT.properties 2021-05-25 19:12:34 +00:00
Northmoc
66ce27593b add support for multiple called flips to FlipCoinEffect 2021-05-25 13:57:33 -04:00
Northmoc
9a62690627 yusri_fortunes_flame.txt 2021-05-25 13:55:33 -04:00
Northmoc
26dcee20dc ravenous_squirrel.txt (Suthro) 2021-05-25 09:29:01 -04:00
Northmoc
8c73de6f94 squirrel_sovereign.txt (Suthro) 2021-05-25 09:26:05 -04:00
Northmoc
8a3a3d05c4 squirrel_sanctuary.txt (Suthro) 2021-05-25 09:22:19 -04:00
Northmoc
11d78d0b44 spreading_insurrection.txt (Suthro) 2021-05-25 08:51:25 -04:00
Northmoc
ae3fb2d41e tourachs_canticle.txt (Suthro) 2021-05-25 08:48:54 -04:00
Northmoc
54d66c0119 lucid_dreams.txt (Suthro) 2021-05-25 08:45:18 -04:00
Northmoc
beb37bad9a Support for "NoColoredMana" 2021-05-25 08:41:40 -04:00
Northmoc
cb25353e60 void_mirror.txt 2021-05-25 08:41:39 -04:00
Northmoc
7bd24f52c7 late_to_dinner.txt (Suthro) 2021-05-25 08:40:47 -04:00
Northmoc
bfd646f409 break_ties.txt (Suthro) 2021-05-25 08:40:46 -04:00
Northmoc
ec5ade7b38 chatterfang_squirrel_general.txt 2021-05-25 08:40:45 -04:00
Northmoc
4bb6a64153 fractured_sanity.txt 2021-05-25 08:39:25 -04:00
John
e7a29f6626 Update lolth_spider_queen.txt 2021-05-25 10:50:28 +00:00
John
7d168d7f24 Update timeless_dragon.txt 2021-05-25 10:16:53 +00:00
John
8b0e10442f Update unmarked_grave.txt 2021-05-25 10:16:28 +00:00
John
10ed279650 Update lolth_spider_queen.txt 2021-05-25 10:11:05 +00:00
John
18300fb1a2 Update flametongue_yearling.txt 2021-05-25 10:08:46 +00:00
John
82543ee67e Update dakkon_shadow_slayer.txt 2021-05-25 10:07:58 +00:00
John
58c8a23279 Update dakkon_shadow_slayer.txt 2021-05-25 10:07:23 +00:00
Michael Kamensky
6b7db83a40 Merge branch 'collector-number-in-card-list-and-card-db-refactoring' into 'master'
Cards CollectorNumber plus Miscs

See merge request core-developers/forge!4743
2021-05-25 06:23:30 +00:00
Michael Kamensky
3d5a7e9bc7 Merge branch 'foul' into 'master'
Foul Emissary: Fix NPE

See merge request core-developers/forge!4739
2021-05-25 06:21:36 +00:00
Michael Kamensky
d1e8f9db6b Merge branch 'prevent' into 'master'
Update check for detecting prevented damage

See merge request core-developers/forge!4742
2021-05-25 06:21:23 +00:00
Michael Kamensky
532e6bee5b Merge branch 'kevlahnota-master-patch-82009' into 'master'
fix typo

See merge request core-developers/forge!4744
2021-05-25 06:20:48 +00:00
Michael Kamensky
a7af611dfb Merge branch 'rise' into 'master'
MH2: Rise and Shine and support

See merge request core-developers/forge!4745
2021-05-25 06:20:42 +00:00
Northmoc
de1296f750 add support for "RememberAnimated" to AnimateEffectBase 2021-05-24 22:06:55 -04:00
Northmoc
43e1c20046 rise_and_shine.txt 2021-05-24 22:06:55 -04:00
Anthony Calosa
b285205376 fix typo 2021-05-24 23:39:22 +00:00
leriomaggio
db4ec5eb07 Merge branch 'collector-number-in-card-list-and-card-db-refactoring' of git.cardforge.org:leriomaggio/forge into collector-number-in-card-list-and-card-db-refactoring 2021-05-24 21:10:19 +01:00
leriomaggio
51e6b6c908 Merge remote-tracking branch 'upstream/master' into collector-number-in-card-list-and-card-db-refactoring 2021-05-24 21:09:58 +01:00
leriomaggio
e57ab4cdb0 Updated missing collector numbers from scryfall 2021-05-24 21:04:39 +01:00
leriomaggio
c4d4a7f2c3 Resetting ImageFetcher to use collectorNumber as string 2021-05-24 19:37:25 +01:00
leriomaggio
66045abf16 Resetting ImageFetcher to use collectorNumber as string 2021-05-24 19:36:24 +01:00
leriomaggio
019af5d238 Robust sorting of CollectorNumber (as string) handling properly non-numerical nrs. and N.A.s 2021-05-24 15:50:54 +01:00
leriomaggio
d71d10a97f Improved translations for CollectorNumber in Italian and Spanish, and while at it...
...I got rid of duplication for the lblSet col, while also improving translations.
In particular, the "lblSetEdition" column was translated as the "Mistery column" (for some reason :D), whereas a second lblSet2 was instead defined and used, namely "the label tooltip for the Set Columndef".
On this note, this col was also mistranslated in ES, IT, DE - those are the language I could handle so far.
In fact "Set" was intended (and so translated) as "to set" instead as for "Expansion/Edition".
Corrected accordingly! :)
2021-05-24 15:02:25 +01:00
tool4EvEr
f374466d0a Update check for detecting prevented damage 2021-05-24 14:31:15 +02:00
tool4EvEr
97d9c57951 Update trigger so it doesn't resolve when card moved 2021-05-24 14:04:46 +02:00
leriomaggio
b651c1f9e9 Added CollectorNumberColumn to pileBy menu. 2021-05-24 11:49:05 +01:00
leriomaggio
d448b59dcc Just code formatting 2021-05-24 11:48:22 +01:00
John
06ccc2d9c6 Update TypeLists.txt 2021-05-24 10:46:43 +00:00
John
bf2ee271fd Add new file 2021-05-24 10:41:57 +00:00
John
b9486e8853 Add new file 2021-05-24 10:41:25 +00:00
John
7911f9bd20 Add new file 2021-05-24 10:40:46 +00:00
John
6753039b92 Add new file 2021-05-24 10:40:15 +00:00
John
802eff0796 Add new file 2021-05-24 10:39:48 +00:00
John
ff8141705d Add new file 2021-05-24 10:39:20 +00:00
John
b28f8f50e1 Add new file 2021-05-24 10:38:41 +00:00
John
49a1f9527c Add new file 2021-05-24 10:36:58 +00:00
John
240f5e6419 Add new file 2021-05-24 10:35:47 +00:00
tool4EvEr
2f26cbe676 Fix NPE 2021-05-24 12:24:15 +02:00
leriomaggio
2a304bd413 Made sorting on CN more reliable with zero-padded strings 2021-05-24 10:14:22 +01:00
leriomaggio
189b0ea122 Formatting adjustments 2021-05-24 10:13:40 +01:00
tool4EvEr
70b24f9b86 Fix Sen Triplets interaction 2021-05-24 10:55:51 +02:00
Anthony Calosa
6c11701032 Merge branch 'master' into 'master'
cleanup

See merge request core-developers/forge!4738
2021-05-24 08:24:21 +00:00
Anthony Calosa
d4a28c0e7b uncomment 2021-05-24 16:23:23 +08:00
Anthony Calosa
0e50de91b4 cleanup 2021-05-24 16:22:09 +08:00
Anthony Calosa
ac78b96c2b Merge branch 'master' into 'master'
[Mobile] Refactor preloading ItemPool

See merge request core-developers/forge!4737
2021-05-24 08:13:25 +00:00
Anthony Calosa
aca091e3c3 [Mobile] Refactor preloading ItemPool
Dont preload ItemPool on Android devices with less than 5GB RAM and Desktop/Mobile port
2021-05-24 16:09:40 +08:00
leriomaggio
572e7f5be8 Updated Card Edition Resources with Collector Numbers 2021-05-24 07:17:16 +01:00
tool4EvEr
ef4ab9570a Clean up 2021-05-24 08:13:09 +02:00
Michael Kamensky
6207299219 Merge branch 'cleanup' into 'master'
Another few small non-functional changes to clean up some redundancy

See merge request core-developers/forge!4734
2021-05-24 03:29:07 +00:00
Michael Kamensky
8eb8c666b6 Merge branch 'hellkite' into 'master'
Refactor CardDamageHistory

See merge request core-developers/forge!4711
2021-05-24 03:28:58 +00:00
Bug Hunter
8f40e8ae04 Refactor CardDamageHistory 2021-05-24 03:28:57 +00:00
Michael Kamensky
b93131b990 Merge branch 'fixai' into 'master'
ChangeZoneAi: Fix casting without paying mana cost using X values

See merge request core-developers/forge!4735
2021-05-24 03:28:37 +00:00
tool4EvEr
6bce621da4 Fix Fires of Ivention 2021-05-24 00:42:34 +02:00
tool4EvEr
9e9bc5193f Cleanup 2021-05-23 23:23:42 +02:00
tool4EvEr
178f937727 Fix AI casting without paying mana cost using X values 2021-05-23 22:45:46 +02:00
Michael Kamensky
d94abfaef9 Merge branch 'Williams-master-patch-16712' into 'master'
Update Resale Promos.txt

See merge request core-developers/forge!4733
2021-05-23 17:26:57 +00:00
leriomaggio
c03da7eb74 Revert to CollectorNumber in PaperCard as String to handle cases of mulitple art (e.g. 21a). 2021-05-23 16:52:12 +01:00
tool4EvEr
c39313d973 Clean up 2021-05-23 17:36:07 +02:00
leriomaggio
a0c126f39e Merge remote-tracking branch 'upstream/master' into collector-number-in-card-list-and-card-db-refactoring 2021-05-23 14:50:49 +01:00
leriomaggio
354d184664 Display new collectorNumber info in Column List view for cards
This addresses Issue #1846
Further changes are due to CardEditions missing collector number information
2021-05-23 14:45:29 +01:00
leriomaggio
0e49d103b1 Update to the new getFoiledMethod in PaperCard API 2021-05-23 14:34:38 +01:00
leriomaggio
fbbae218c0 Integration with PaperCard CollectorNumber, and FIX to new Scryfall API specs 2021-05-23 14:33:48 +01:00
leriomaggio
12387f0efb Refactoring of PaperCard API for collectorNumber and GetFoil.
PaperCard instance now includes a new attribute (i.e. `collectorNumber`) that will be used later on to fill in the "CN" Column in List view (see `ColumnDef.java`).
To allow for minor disruption to existing API, the collectorNumber attribute will be handled in a _Property-like_ fashion via the getter method (i.e. `getCollectorNumber`).
In particular, if collectorNumber is marked as `UNSET` (i.e. `PaperCard.UNSET_COLLECTOR_NUMBER`), the corresponding value will be automatically retrieved from the matched `CardEdition`, and updated for later use.
Nevertheless, Constructors including the new parameter have been also set for future API change.
Moreover, the new attribute has been also considered in methods for object comparison, for a more accurate sorting criterion.

The second new addition to PaperCard API regards FOIL cards (i.e. `getFoiled()`).
This method was originally part of `CardDb` API as described in ISSUE #1848.
With this API change, an internal reference to a `PaperCard` (foil) instance will be used to return the foil version of "itself" for each PaperCard Instance. This will avoid proliferation of PaperCard instances in memory, while keeping all information consistent with the original "unfoiled" card.
NOTE: at the moment, every card can be Foiled, with no restriction whatsoever on the _actual_ existence of original (true) PaperCard.
2021-05-23 13:07:39 +01:00
leriomaggio
41be0bb069 PaperToken aligned with the new IPaperCard Interface (collectorNr always to default for tokens) 2021-05-23 12:56:10 +01:00
leriomaggio
6edcb95aab Refactor IPaperCard Interface to include collectorNumber
The IPaperCard interface now includes a method to return the Collector Number of a Card.
Moreover, two new constants have been included for Default Art Index (i.e. 1) and a flag to mark the absence of a Collector Number (i.e. 0).
2021-05-23 12:55:15 +01:00
John
f022419ceb Update Resale Promos.txt 2021-05-23 11:42:11 +00:00
Michael Kamensky
9232042b02 Merge branch 'exception-thrown-when-discard-card-when-more-then-eight' into 'master'
Exception thrown when discard card when more then eight

See merge request core-developers/forge!4732
2021-05-23 04:17:34 +00:00
brngl
1c642a3cb2 corrected a wrong translation 2021-05-23 02:30:09 +02:00
brngl
b5e86473a4 corrected a wrong translation 2021-05-23 01:24:37 +02:00
Michael Kamensky
8417f5cb7a Merge branch 'counterai' into 'master'
CounterAi: Fix AI casting without paying mana cost using X values

See merge request core-developers/forge!4731
2021-05-22 17:41:59 +00:00
tool4EvEr
f434be45b6 Fix AI casting without paying mana cost using X values 2021-05-22 19:38:55 +02:00
Michael Kamensky
6d493aeb9c Merge branch 'paid' into 'master'
Fix Plumb the Forbidden

Closes #1849

See merge request core-developers/forge!4730
2021-05-22 09:32:22 +00:00
Michael Kamensky
b4052039e3 Merge branch 'cards-2021-05-20' into 'master'
Drizzt Do'Urden

See merge request core-developers/forge!4728
2021-05-22 09:32:00 +00:00
tool4EvEr
727689a3bd Fix Plumb the Forbidden 2021-05-22 11:02:57 +02:00
Hythonia
b86f9792f3 Drizzt: DeckHas 2021-05-22 09:48:26 +02:00
Michael Kamensky
83f4a5ea73 Merge branch 'playeffect' into 'master'
PlayEffect: avoid confirmAction

Closes #843

See merge request core-developers/forge!4729
2021-05-22 06:05:05 +00:00
tool4EvEr
80ddea2c8c Clean up 2021-05-21 23:07:42 +02:00
tool4EvEr
6618f71297 PlayEffect: avoid confirmAction 2021-05-21 23:03:33 +02:00
Hythonia
5c1c039188 AFR: Drizzit and the Spider token 2021-05-21 19:51:16 +02:00
Michael Kamensky
af1afb5348 Merge branch 'lki' into 'master'
Fix Luminate Primordial

See merge request core-developers/forge!4727
2021-05-21 17:30:23 +00:00
tool4EvEr
5bbd527837 Fix LKI 2021-05-21 12:16:53 +02:00
Michael Kamensky
2eb71fe1c9 Merge branch 'fixup' into 'master'
CombatUtil: Fix AI workaround

See merge request core-developers/forge!4726
2021-05-21 07:48:27 +00:00
Michael Kamensky
e1157ad01a Merge branch 'Williams-master-patch-78424' into 'master'
Update 2021 Lunar New Year.txt

See merge request core-developers/forge!4724
2021-05-21 07:48:23 +00:00
tool4EvEr
e5df3b9cb3 Fix workaround 2021-05-21 08:43:36 +02:00
Anthony Calosa
a52f7dc540 Merge branch 'master' into 'master'
[Mobile] Disable loading of HD Textures for Devices with RAM < 5GB

See merge request core-developers/forge!4725
2021-05-21 03:03:48 +00:00
Anthony Calosa
eff69eba7d Merge remote-tracking branch 'remotes/core/master' 2021-05-21 10:58:27 +08:00
Anthony Calosa
f8a41b39ec [Mobile] Disable loading of HD Textures for Devices with RAM < 5GB 2021-05-21 10:58:03 +08:00
John
cd31806100 Update 2021 Lunar New Year.txt 2021-05-20 12:48:54 +00:00
Bug Hunter
e5d588c4eb Merge branch 'TRT-master-patch-14456' into 'master'
Update forge-gui/res/cardsfolder/b/battlemages_bracers.txt

See merge request core-developers/forge!4723
2021-05-20 12:44:39 +00:00
Bug Hunter
3b0483752c Update forge-gui/res/cardsfolder/b/battlemages_bracers.txt 2021-05-20 12:43:42 +00:00
Anthony Calosa
5ea5ca0ef7 Merge branch 'master' into 'master'
Net Decks Archive Updates

See merge request core-developers/forge!4722
2021-05-20 12:13:35 +00:00
Churrufli
08bd2c4724 Net Decks Archive Updates 2021-05-20 12:42:24 +02:00
Michael Kamensky
1c1a4d92f4 Merge branch 'bracers' into 'master'
Illusionist's Bracers: Fix triggering for Card that's removed while paying activation cost

See merge request core-developers/forge!4721
2021-05-20 09:21:36 +00:00
Michael Kamensky
37e5dda38f Merge branch 'npefix' into 'master'
chooseSomeType: Fix NPE

See merge request core-developers/forge!4715
2021-05-20 04:53:12 +00:00
Michael Kamensky
be6232ee82 Merge branch 'fixcount' into 'master'
Cleanup xCount

See merge request core-developers/forge!4718
2021-05-20 04:52:29 +00:00
Michael Kamensky
651fa42e4a Merge branch 'guile' into 'master'
Kaya's Guile: Fix moving own cards

See merge request core-developers/forge!4720
2021-05-20 04:52:01 +00:00
Michael Kamensky
c69d261e56 Merge branch 'rescue' into 'master'
Fix Rescue from the Underworld

See merge request core-developers/forge!4719
2021-05-20 04:51:55 +00:00
Michael Kamensky
54446949a5 Merge branch 'combats' into 'master'
Fix deadlock for impossible blocking requirements

See merge request core-developers/forge!4681
2021-05-20 04:51:42 +00:00
Michael Kamensky
161d4ed314 Merge branch 'angel' into 'master'
Fix paying negative life

See merge request core-developers/forge!4716
2021-05-20 04:45:40 +00:00
tool4EvEr
a88c08bf3f Fix triggering for Card that's removed while paying activation cost 2021-05-19 23:07:31 +02:00
tool4EvEr
eae4a0be35 Fix moving own cards 2021-05-19 20:54:58 +02:00
tool4EvEr
0e28b40c25 Fix Rescue from the Underworld 2021-05-19 20:28:42 +02:00
tool4EvEr
f8adbcc252 Cleanup xCount 2021-05-19 20:03:17 +02:00
tool4EvEr
6f9d557a64 Clean up 2021-05-19 16:29:38 +02:00
Michael Kamensky
ac939de42f Merge branch 'italian-translations-fix' into 'master'
Italian translations fix

See merge request core-developers/forge!4717
2021-05-19 03:53:12 +00:00
Michael Kamensky
82756e1073 Merge branch 'cardfixes' into 'master'
Minor card fixes

See merge request core-developers/forge!4714
2021-05-19 03:52:38 +00:00
brngl
1b14148b0f corrected some wrong translations 2021-05-19 00:23:18 +02:00
brngl
0cfb5eff0e corrected some wrong translations 2021-05-19 00:15:38 +02:00
tool4EvEr
ce3bab8a33 Fix for Temporal Extortion 2021-05-19 00:09:55 +02:00
tool4EvEr
e56b8545c7 Fix paying negative life 2021-05-18 23:36:56 +02:00
tool4EvEr
ac7fc21db5 Fix NPE 2021-05-18 22:58:56 +02:00
Michael Kamensky
48bc2174a4 Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!4709
2021-05-18 19:34:07 +00:00
Michael Kamensky
d11d58889b Merge branch 'kevlahnota-master-patch-22574' into 'master'
NoSuchMethodError forge.game.trigger.TriggerAbilityTriggered in addTriggeringObject

See merge request core-developers/forge!4710
2021-05-18 19:33:58 +00:00
Michael Kamensky
8cf01c318a Merge branch 'cleanup' into 'master'
Fix onCleanupPhase skipping losers

See merge request core-developers/forge!4712
2021-05-18 19:33:46 +00:00
Michael Kamensky
e18007e902 Merge branch 'fixnpe' into 'master'
UntapAi: Fix NPE

See merge request core-developers/forge!4713
2021-05-18 19:33:37 +00:00
Hythonia
1f13b8d43d Minor card fixes 2021-05-18 20:11:24 +02:00
tool4EvEr
c6b82043d0 Fix NPE 2021-05-18 19:29:37 +02:00
tool4EvEr
671937ff7f Fix onCleanupPhase skipping losers 2021-05-18 19:07:54 +02:00
Anthony Calosa
27478966bc NoSuchMethodError forge.game.trigger.TriggerAbilityTriggered in addTriggeringObject 2021-05-18 14:43:43 +00:00
CCTV-1
442ea94cde translate "AltZoneTabs" 2021-05-18 18:47:29 +08:00
Michael Kamensky
13bd24ef83 Merge branch 'changezone' into 'master'
ChangeZoneEffect: Don't modify tapped when replaced

See merge request core-developers/forge!4704
2021-05-18 03:50:25 +00:00
Bug Hunter
32240b6004 ChangeZoneEffect: Don't modify tapped when replaced 2021-05-18 03:50:24 +00:00
tool4EvEr
abd0880c66 Fix deadlock for impossible blocking requirements 2021-05-17 17:51:09 +02:00
Hans Mackowiak
bb6c5f8e44 Merge branch '1840-npe-at-forge-game-ability-abilityutils-xcount-abilityutils-java-1677' into 'master'
Resolve "NPE at forge.game.ability.AbilityUtils.xCount(AbilityUtils.java:1677)"

Closes #1840

See merge request core-developers/forge!4703
2021-05-16 13:56:21 +00:00
Bug Hunter
66d0141fae Merge branch 'fixcard' into 'master'
Fix card

See merge request core-developers/forge!4707
2021-05-16 13:02:04 +00:00
tool4EvEr
57cf7a582b Fix card 2021-05-16 15:02:00 +02:00
Michael Kamensky
706827fc0c Merge branch 'Williams-master-patch-55192' into 'master'
Update Secret Lair Ultimate Edition.txt

See merge request core-developers/forge!4706
2021-05-16 12:51:43 +00:00
Michael Kamensky
54009dcb06 Merge branch 'aicleanup' into 'master'
Minor cleanup

See merge request core-developers/forge!4705
2021-05-16 12:43:17 +00:00
John
1049deb299 Update Secret Lair Ultimate Edition.txt 2021-05-16 12:43:06 +00:00
tool4EvEr
19b928782a Clean import 2021-05-16 12:59:13 +02:00
tool4EvEr
4aafafb5bd Minor cleanup 2021-05-16 12:52:02 +02:00
leriomaggio
65ff5a8c51 Added Spotlight rubbish to ignore list 2021-05-16 11:46:23 +01:00
Hans Mackowiak
d2449c181a AiController: need to set triggered Object for ETB check 2021-05-16 09:18:31 +02:00
Hans Mackowiak
9527a33a58 Merge branch 'refactor_damage_replacement' into 'master'
Deal damage use damage map

See merge request core-developers/forge!4696
2021-05-16 06:51:39 +00:00
Alumi
419a513d8f Deal damage use damage map 2021-05-16 06:51:38 +00:00
Michael Kamensky
a1e7ecbecc Merge branch 'etali' into 'master'
PlayEffect: Fix loop of selecting same card when optional

Closes #1835

See merge request core-developers/forge!4697
2021-05-16 03:47:07 +00:00
Michael Kamensky
f996c90653 Merge branch 'nextturn' into 'master'
Refactor addUntilCommand

See merge request core-developers/forge!4702
2021-05-15 17:33:05 +00:00
Bug Hunter
be2a7f5a51 Refactor addUntilCommand 2021-05-15 17:33:03 +00:00
Michael Kamensky
a6ca193ae0 Merge branch 'cardPropertyTargetedPlayer' into 'master'
Card property targeted player

See merge request core-developers/forge!4606
2021-05-15 15:27:16 +00:00
Hans Mackowiak
ff7f81c307 Card property targeted player 2021-05-15 15:27:15 +00:00
Michael Kamensky
cad42948cd Merge branch '1836-npe-at-forge-game-gameaction-sacrificedestroy-gameaction-java-1709' into 'master'
Resolve "NPE at forge.game.GameAction.sacrificeDestroy(GameAction.java:1709)"

Closes #1836

See merge request core-developers/forge!4699
2021-05-15 12:40:13 +00:00
Hans Mackowiak
5ad88099e1 Resolve "NPE at forge.game.GameAction.sacrificeDestroy(GameAction.java:1709)" 2021-05-15 12:40:12 +00:00
Sol
c849c2b867 Merge branch 'scourge' into 'master'
Fix Boneyard Scourge

See merge request core-developers/forge!4700
2021-05-15 03:23:35 +00:00
Michael Kamensky
1571263575 Merge branch 'addtocombat' into 'master'
Fix addToCombat not checking for changes in defenders

See merge request core-developers/forge!4701
2021-05-14 17:35:06 +00:00
tool4EvEr
a335d60fcb Fix addToCombat not checking for changes in defenders 2021-05-14 16:17:21 +02:00
tool4EvEr
c78c182e32 Fix trigger 2021-05-14 15:15:50 +02:00
Michael Kamensky
8ff5d05078 Merge branch 'wall' into 'master'
Fix trigger running with no zone for Wall of Stolen Identity

See merge request core-developers/forge!4666
2021-05-14 05:39:09 +00:00
Bug Hunter
80a1c13a07 Fix trigger running with no zone for Wall of Stolen Identity 2021-05-14 05:39:08 +00:00
Hans Mackowiak
0b6ed46a0a Merge branch 'basecamp' into 'master'
Fix Base Camp

See merge request core-developers/forge!4698
2021-05-13 19:29:57 +00:00
tool4EvEr
cdaba3483a Fix Base Camp 2021-05-13 14:50:32 +02:00
tool4EvEr
3681dba5fb Fix loop of selecting same card when optional 2021-05-13 13:55:19 +02:00
Hans Mackowiak
ddbe5af21c Merge branch 'TRT-master-patch-01974' into 'master'
Update forge-gui/res/cardsfolder/r/rot_hulk.txt

See merge request core-developers/forge!4695
2021-05-12 14:52:37 +00:00
Hans Mackowiak
da4263131e Merge branch 'elspeth' into 'master'
Fix stateBasedAction_Saga

Closes #1831

See merge request core-developers/forge!4693
2021-05-12 14:47:33 +00:00
Bug Hunter
f3b771cb7d Fix stateBasedAction_Saga 2021-05-12 14:47:32 +00:00
Bug Hunter
39c764e283 Update forge-gui/res/cardsfolder/r/rot_hulk.txt 2021-05-12 07:38:40 +00:00
Michael Kamensky
9d4e02c2d4 Merge branch 'defenderStatic' into 'master'
StaticAbilities: moved Defender into CantAttack

See merge request core-developers/forge!4563
2021-05-12 06:35:16 +00:00
Hans Mackowiak
34ab157046 StaticAbilities: moved Defender into CantAttack 2021-05-12 06:35:15 +00:00
Michael Kamensky
e76403f54f Merge branch 'ruxa' into 'master'
Fix Muraganda Petroglyphs not affecting face-down with morph

See merge request core-developers/forge!4692
2021-05-12 05:23:50 +00:00
Michael Kamensky
3f7b1ce204 Merge branch 'fix_spellskite' into 'master'
Fix BecomesTarget not triggering for Spellskite's ability

See merge request core-developers/forge!4694
2021-05-12 05:23:42 +00:00
Lyu Zong-Hong
0eff1225b8 Fix BecomesTarget not triggering for Spellskite's ability 2021-05-12 09:26:06 +09:00
tool4EvEr
4e2d024b2a Fix Muraganda Petroglyphs not affecting face-down with morph 2021-05-11 20:11:26 +02:00
Michael Kamensky
1db289c35e Merge branch 'soulecho' into 'master'
Fix Soul Echo

See merge request core-developers/forge!4691
2021-05-11 18:10:46 +00:00
tool4EvEr
c8a4a41eaf Fix Soul Echo 2021-05-11 19:51:00 +02:00
Michael Kamensky
6221f87b8c Merge branch 'master' into 'master'
Net Decks Archive Updates

See merge request core-developers/forge!4689
2021-05-11 16:48:10 +00:00
Michael Kamensky
ab3b682c47 Merge branch 'tokencards' into 'master'
Add Time Sidewalk and Bone Rattler

See merge request core-developers/forge!4690
2021-05-11 16:48:08 +00:00
Michael Kamensky
38d6ab9895 Merge branch 'refactor_cant_be_prevented' into 'master'
Refactor damage can't be prevented effect

Closes #1232

See merge request core-developers/forge!4686
2021-05-11 16:47:55 +00:00
Alumi
4d6da7454c Refactor damage can't be prevented effect 2021-05-11 16:47:54 +00:00
Churrufli
19ac30eed0 Net Decks Archive Updates 2021-05-11 09:29:42 +02:00
Hythonia
84a7b79e3c Add Time Sidewalk and Bone Rattler 2021-05-11 09:12:05 +02:00
Michael Kamensky
e2fc8015b8 Merge branch 'twosat-master-patch-29098' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4687
2021-05-11 03:46:02 +00:00
Hans Mackowiak
5aac30f44a Merge branch 'colors' into 'master'
ColorIdentity: Check for CDA text

See merge request core-developers/forge!4672
2021-05-10 19:36:09 +00:00
Bug Hunter
d9340349fa ColorIdentity: Check for CDA text 2021-05-10 19:36:09 +00:00
Hans Mackowiak
82019b5f40 Merge branch 'samename' into 'master'
ChangeZoneAi: Fix crash

Closes #1830

See merge request core-developers/forge!4688
2021-05-10 19:35:22 +00:00
tool4EvEr
06e0a82927 Fix crash 2021-05-10 21:17:17 +02:00
Andreas Bendel
53409a6c70 Update de-DE.properties
translated SwitchStates and AltZoneTabs
2021-05-10 18:25:37 +00:00
Hans Mackowiak
59b68d80ca Merge branch 'servant' into 'master'
Fix Grindstone & Painter's Servant interaction

See merge request core-developers/forge!4679
2021-05-10 16:51:06 +00:00
Bug Hunter
4f38f5a389 Fix Grindstone & Painter's Servant interaction 2021-05-10 16:51:06 +00:00
Anthony Calosa
7a0bb07fc1 Merge branch 'kevlahnota-master-patch-01624' into 'master'
unused import

See merge request core-developers/forge!4685
2021-05-10 07:50:36 +00:00
Anthony Calosa
b1d3e712be unused import 2021-05-10 07:50:23 +00:00
Anthony Calosa
bc0cd4a783 Merge branch 'prevent' into 'master'
prevent damage bug

See merge request core-developers/forge!4682
2021-05-10 07:46:32 +00:00
Michael Kamensky
d4dc65c45f Merge branch 'show_remaining_shield' into 'master'
Show remaining shields amount for damage prevention and redirection effect

See merge request core-developers/forge!4684
2021-05-10 03:27:03 +00:00
Michael Kamensky
dd40a1de6b Merge branch 'update_prevention_predition' into 'master'
Update AI damage prevention prediction code

See merge request core-developers/forge!4683
2021-05-10 03:26:41 +00:00
Lyu Zong-Hong
ea65a3423e Show remaining shields amount for damage prevention and redirection effect 2021-05-10 10:04:45 +09:00
Lyu Zong-Hong
04811da9cc Update AI damage prevention prediction code 2021-05-10 08:09:27 +09:00
tool4EvEr
46a4243093 prevent damage bug 2021-05-09 22:47:31 +02:00
Michael Kamensky
03bee3e11e Merge branch 'refactor_fog_protection' into 'master'
Refactor Damage Prevention - Step 3

See merge request core-developers/forge!4680
2021-05-09 18:15:44 +00:00
Lyu Zong-Hong
20491e1136 Refactor fog and protection to use replacement effect 2021-05-09 20:17:26 +09:00
Michael Kamensky
4ea01ea135 Merge branch 'new_sets' into 'master'
Initial edition files for MH2, AFR, PWP21

See merge request core-developers/forge!4671
2021-05-09 04:01:31 +00:00
Michael Kamensky
8440f72e9e Merge branch 'cascade' into 'master'
PlayAi confirmAction only when card is worth it

See merge request core-developers/forge!4661
2021-05-09 04:01:22 +00:00
Michael Kamensky
65291259ee Merge branch 'fix_get_total_shield_npe' into 'master'
Fix NPE in getTotalPreventionShieldAmount

Closes #1827

See merge request core-developers/forge!4678
2021-05-09 04:00:20 +00:00
Lyu Zong-Hong
d42ee05740 Fix NPE in getTotalPreventionShieldAmount 2021-05-09 07:46:04 +09:00
Michael Kamensky
c4bf644240 Merge branch 'refactor_static_prevention' into 'master'
Refactor Damage Prevention - Step 2

See merge request core-developers/forge!4676
2021-05-08 13:57:04 +00:00
Alumi
fe95ad2e11 Refactor Damage Prevention - Step 2 2021-05-08 13:57:03 +00:00
Michael Kamensky
644f80df57 Merge branch 'Williams-master-patch-45382' into 'master'
Update Resale Promos.txt

See merge request core-developers/forge!4677
2021-05-08 13:56:17 +00:00
Michael Kamensky
46e5c3233a Merge branch 'master' into 'master'
Spoiled cards from MH2 and AFR

See merge request core-developers/forge!4674
2021-05-08 13:56:08 +00:00
John
1da27edde6 Update Resale Promos.txt 2021-05-08 09:37:38 +00:00
Hythonia
75da8833dc Urza's Saga fix 2021-05-08 10:22:33 +02:00
Michael Kamensky
c40a132811 Merge branch 'refactor_prevention' into 'master'
Refactor Damage Prevention - Step 1

See merge request core-developers/forge!4675
2021-05-08 04:34:44 +00:00
Alumi
288267ae11 Refactor Damage Prevention - Step 1 2021-05-08 04:34:42 +00:00
Michael Kamensky
8f1a493327 Merge branch 'digreveal' into 'master'
add support to Dig for Reveal$ False

See merge request core-developers/forge!4667
2021-05-08 04:34:08 +00:00
Northmoc
f97adc325f simplify boolean function 2021-05-07 15:06:40 -04:00
Hythonia
8aa72e9397 MH2 and AFR 2021-05-07 19:45:55 +02:00
tool4EvEr
3ab7886eab Refactor confirmAction 2021-05-07 18:14:20 +02:00
paul_snoops
551968a38c Initial edition files for MH2, AFR, PWP21 2021-05-07 15:37:18 +01:00
paul_snoops
e954102f81 Initial edition files for MH2, AFR, PWP21 2021-05-07 14:23:04 +01:00
Michael Kamensky
5a1d5c0a2d Merge branch 'master' into 'master'
Net Decks Archive Updates

See merge request core-developers/forge!4670
2021-05-07 13:11:36 +00:00
paul_snoops
42b553266a Initial edition files for MH2, AFR, PWP21 2021-05-07 09:26:45 +01:00
Churrufli
d98d2915ca Net Deck Archive Updates 2021-05-07 09:18:14 +02:00
Anthony Calosa
634dced5aa Merge branch 'master' into 'master'
[Mobile] NullPointerException forge.toolbox.FChoiceList in showAlternate

See merge request core-developers/forge!4669
2021-05-07 00:48:38 +00:00
Anthony Calosa
5ee94199bb Merge branch 'fixturns' into 'master'
Fix multiplayer extra turns deadlock

See merge request core-developers/forge!4665
2021-05-07 00:48:21 +00:00
Anthony Calosa
23422220ce [Mobile] update check 2021-05-07 08:43:40 +08:00
Sol
c141462432 Merge branch 'migrate-upcoming' into 'master'
Migrate STX and other scripts to alpha folders

See merge request core-developers/forge!4668
2021-05-07 00:43:28 +00:00
Anthony Calosa
41abf876a2 [Mobile] Fix NPE on FChoiceList.java 2021-05-07 08:42:17 +08:00
friarsol
28793abc7a Migrate STX and other scripts to alpha folders 2021-05-06 20:42:17 -04:00
Northmoc
1ce213004a add support to Dig for Reveal$ False 2021-05-06 17:23:45 -04:00
tool4EvEr
e223fa2002 Fix extra turns deadlock 2021-05-06 21:39:24 +02:00
Michael Kamensky
6fc542ff7d Merge branch 'clash' into 'master'
ClashEffect: Fix Triggers

Closes #1821

See merge request core-developers/forge!4662
2021-05-06 03:46:14 +00:00
Michael Kamensky
a1dd4ab0e9 Merge branch 'escalate' into 'master'
Fix Escalate Cost

Closes #1819

See merge request core-developers/forge!4663
2021-05-06 03:45:50 +00:00
tool4EvEr
1232f1be28 Fix Escalate Cost 2021-05-05 22:20:57 +02:00
tool4EvEr
738c7b968a Fix Triggers 2021-05-05 21:59:01 +02:00
tool4EvEr
d6e7d1ffee Check if card for PlayEffect is worth it 2021-05-05 21:14:15 +02:00
Michael Kamensky
6fcdee14e4 Merge branch 'master' into 'master'
Better AI for SpecificCard Auras

See merge request core-developers/forge!4660
2021-05-05 17:47:31 +00:00
Hythonia
8c139ad044 Better AI for SpecificCard Auras, attempt 2 2021-05-05 15:47:43 +02:00
Michael Kamensky
122baba999 Merge branch 'master' into 'master'
Update hyperlinks to point to the git.cardforge.org wiki pages instead of the old slightlymagic.net

See merge request core-developers/forge!4659
2021-05-05 13:06:29 +00:00
Jimmy
27b12f5263 Update hyperlinks to point to the git.cardforge.org wiki pages instead of the old slightlymagic.net 2021-05-05 13:06:28 +00:00
Hythonia
a801424b35 Better AI for SpecificCard Auras 2021-05-05 14:48:40 +02:00
Hans Mackowiak
717a3eaede Merge branch 'fixstatic' into 'master'
Fix StaticEffect missing changes from RemoveLandTypes

See merge request core-developers/forge!4655
2021-05-05 10:41:02 +00:00
Anthony Calosa
0061f35b46 Merge branch 'master' into 'master'
[Mobile] add Alternate Player Zone Layout (Landscape)

See merge request core-developers/forge!4658
2021-05-05 03:53:49 +00:00
Anthony Calosa
ff20646a63 Merge branch 'escape' into 'master'
Fix Escape Protocol

Closes #1817

See merge request core-developers/forge!4657
2021-05-05 03:53:26 +00:00
Anthony Calosa
a5f4375ff2 Merge branch 'fixpay' into 'master'
Fix playSpellAbilityWithoutPayingManaCost not checking amount modifiers

Closes #1816

See merge request core-developers/forge!4656
2021-05-05 03:53:16 +00:00
Anthony Calosa
99b2cbb606 [Mobile] add Alternate Player Zone Layout (Landscape) 2021-05-05 11:34:36 +08:00
tool4EvEr
15e8eee373 Fix Escape Protocol 2021-05-04 20:38:03 +02:00
tool4EvEr
4cd2bc266a Fix playSpellAbilityWithoutPayingManaCost not checking amount modifiers 2021-05-04 20:30:56 +02:00
Michael Kamensky
92472c815a Merge branch 'partner2' into 'master'
Partner with: better fix

See merge request core-developers/forge!4650
2021-05-04 18:18:02 +00:00
Michael Kamensky
a246257eff Merge branch 'master' into 'master'
Update simplified chinese translation

See merge request core-developers/forge!4652
2021-05-04 18:17:55 +00:00
Michael Kamensky
b6bab552be Merge branch 'fix-auto-updater' into 'master'
Fix AutoUpdater to point to the new snapshot location

See merge request core-developers/forge!4651
2021-05-04 18:17:44 +00:00
Michael Kamensky
1afce00069 Merge branch 'controllers' into 'master'
getAllPossibleAbilities: Restore old controllers

Closes #1806

See merge request core-developers/forge!4646
2021-05-04 18:17:41 +00:00
Michael Kamensky
9ddfdf3638 Merge branch 'master' into 'master'
Better AI logic for Boltlands and more

Closes #1573

See merge request core-developers/forge!4654
2021-05-04 18:17:03 +00:00
tool4EvEr
06230ffa40 Fix StaticEffect missing changes from RemoveLandTypes 2021-05-04 19:21:47 +02:00
Hythonia
cac7ac2a8a Remove UnlessAI from shocklands and boltlands 2021-05-04 12:12:26 +02:00
Hythonia
fbbe4f5579 Magus of the Scroll AI 2021-05-04 10:34:38 +02:00
Hythonia
7d8373643f Remove leftover code 2021-05-04 09:25:35 +02:00
Hythonia
97bf88421e Better AI logic for Boltlands 2021-05-04 09:24:41 +02:00
CCTV-1
c067ab7cc9 translate new setting 2021-05-04 09:38:03 +08:00
friarsol
b21e4f5296 Fix AutoUpdater to point to the new snapshot location 2021-05-03 21:32:55 -04:00
tool4EvEr
ea0add13ad Partner with: cleaner fix 2021-05-03 20:14:56 +02:00
Michael Kamensky
bfc3157186 Merge branch 'partner_with' into 'master'
Partner with support

Closes #1760

See merge request core-developers/forge!4649
2021-05-03 17:22:13 +00:00
tool4EvEr
a5aeb43305 Partner with support 2021-05-03 18:57:42 +02:00
Michael Kamensky
2b77a15845 Merge branch 'wouldPhaseBeSkipped' into 'master'
Update wouldPhaseBeSkipped logic

See merge request core-developers/forge!4648
2021-05-03 15:57:16 +00:00
tool4EvEr
0d1b751600 Update wouldPhaseBeSkipped logic 2021-05-03 17:06:39 +02:00
Bug Hunter
754e2a7aa7 Merge branch 'TRT-master-patch-29542' into 'master'
Update forge-gui/res/cardsfolder/upcoming/flamescroll_celebrant_revel_in_silence.txt

See merge request core-developers/forge!4647
2021-05-03 14:27:36 +00:00
Bug Hunter
731f375332 Update forge-gui/res/cardsfolder/upcoming/flamescroll_celebrant_revel_in_silence.txt 2021-05-03 14:27:12 +00:00
tool4EvEr
faa8013208 Restore old controllers for AI checks 2021-05-02 17:03:44 +02:00
Michael Kamensky
90afe81856 Merge branch 'modaloption' into 'master'
Add preference how to display alternate card states in deck views

See merge request core-developers/forge!4625
2021-05-02 14:25:36 +00:00
Bug Hunter
c13e379908 Add preference how to display alternate card states in deck views 2021-05-02 14:25:35 +00:00
Michael Kamensky
689d3bb9b5 Merge branch 'fixloop' into 'master'
Fix multiplayer loop from SA controlled by loser

See merge request core-developers/forge!4636
2021-05-02 14:25:27 +00:00
Michael Kamensky
9ae8fcf3bc Merge branch 'killing_wave' into 'master'
payCostToPreventEffect ignores X amount

See merge request core-developers/forge!4645
2021-05-02 14:25:16 +00:00
tool4EvEr
74f3a0a5ed Fix payCostToPreventEffect for X amount 2021-05-02 14:38:19 +02:00
Anthony Calosa
d51e20bffe Merge branch 'master' into 'master'
fix localizer

See merge request core-developers/forge!4644
2021-05-02 11:47:15 +00:00
Anthony Calosa
c5d6214ab2 fix localizer 2021-05-02 19:44:19 +08:00
Michael Kamensky
898921ea4f Merge branch 'untapall' into 'master'
UntapAllAi: improve logic (Najeela)

See merge request core-developers/forge!4643
2021-05-02 10:42:12 +00:00
tool4EvEr
8c6194da35 improve logic (Najeela) 2021-05-02 11:55:04 +02:00
Michael Kamensky
8b2a3ccb42 Merge branch 'costfix' into 'master'
Restore Balance: Fix Cost

Closes #1814

See merge request core-developers/forge!4642
2021-05-02 09:47:43 +00:00
tool4EvEr
820c869e2c Avoid clearTriggers 2021-05-02 11:44:41 +02:00
tool4EvEr
9a21a3e2a4 Fix Cost 2021-05-02 11:04:59 +02:00
Hans Mackowiak
03e47cee56 Merge branch 'suspend' into 'master'
AI: Fix backing out of Suspend

See merge request core-developers/forge!4640
2021-05-02 08:55:10 +00:00
Michael Kamensky
1a28f15f75 Merge branch 'NPE' into 'master'
viewDeckList: Fix NPE

See merge request core-developers/forge!4639
2021-05-02 03:55:14 +00:00
Anthony Calosa
72d5c74631 Merge branch 'master' into 'master'
[Mobile] Fix ExceptionInInitializerError forge.screens.home.NewGameMenu$NewGameScreen

See merge request core-developers/forge!4641
2021-05-02 01:20:52 +00:00
Anthony Calosa
322ebdf8a6 Update spacing 2021-05-02 09:16:55 +08:00
Anthony Calosa
3d3296d1b7 [Mobile] Fix ExceptionInInitializerError forge.screens.home.NewGameMenu$NewGameScreen
- some mobile issues reported via Sentry is related to Translation errors and it should return the default original value from English locale.
2021-05-02 09:13:49 +08:00
tool4EvEr
e3715c3f81 Update ceasetoExist check 2021-05-02 00:02:49 +02:00
tool4EvEr
db9e019519 Fix backing out of Suspend 2021-05-01 23:05:52 +02:00
tool4EvEr
3a2ea56c92 Fix NPE 2021-05-01 21:26:49 +02:00
tool4EvEr
df6b552ac1 Fully clear unwanted triggers 2021-05-01 20:36:42 +02:00
Michael Kamensky
9bb4d7ed93 Merge branch 'turns' into 'master'
Update advanceToNextPhase logic

See merge request core-developers/forge!4633
2021-05-01 17:36:38 +00:00
Anthony Calosa
562ef1b147 Merge branch 'kevlahnota-master-patch-03506' into 'master'
Update GuiDownloadZipService.java

See merge request core-developers/forge!4638
2021-05-01 14:27:56 +00:00
Anthony Calosa
6d31a2f656 Update GuiDownloadZipService.java 2021-05-01 14:27:02 +00:00
Bug Hunter
62c16d9f08 Merge branch 'fixcost' into 'master'
Fix typo

Closes #1813

See merge request core-developers/forge!4637
2021-05-01 10:44:55 +00:00
tool4EvEr
db17973ea6 Fix cost 2021-05-01 12:43:26 +02:00
tool4EvEr
8155376c45 Fix multiplayer loop from SA controlled by loser 2021-05-01 12:33:25 +02:00
Anthony Calosa
ae26c7bc7a Merge branch 'master' into 'master'
[Mobile] ExceptionInInitializerError (forge.screens.FScreen in <init>)

See merge request core-developers/forge!4635
2021-05-01 05:17:33 +00:00
Anthony Calosa
fa4e4cb2bb [Mobile] ExceptionInInitializerError (forge.screens.FScreen in <init>)
- it seems it was trying to draw the bug reporter interface while ending the graphics if sentry is not enabled
2021-05-01 13:15:10 +08:00
Hans Mackowiak
7842b7e0fd Merge branch 'ResolvedThisTurn' into 'master'
Fix ResolvedThisTurn

See merge request core-developers/forge!4631
2021-04-30 23:33:44 +00:00
Anthony Calosa
52d6245b4a Merge branch 'master' into 'master'
[Mobile] fix cache capacity initialization

See merge request core-developers/forge!4634
2021-04-30 22:42:19 +00:00
Anthony Calosa
4c03d4f0b4 [Mobile] fix cache capacity initialization 2021-05-01 06:41:04 +08:00
tool4EvEr
de8aaf35a3 Update advanceToNextPhase logic 2021-04-30 23:34:16 +02:00
Anthony Calosa
dda5d8206f Merge branch 'master' into 'master'
[Mobile] fix black image

See merge request core-developers/forge!4632
2021-04-30 18:53:22 +00:00
Anthony Calosa
f8ca2a50b7 [Mobile] fix black image 2021-05-01 02:34:04 +08:00
tool4EvEr
329f4072be Fix ResolvedThisTurn 2021-04-30 19:36:29 +02:00
Anthony Calosa
a88b392a43 Merge branch 'kevlahnota-master-patch-90708' into 'master'
unused Import

See merge request core-developers/forge!4630
2021-04-30 14:47:01 +00:00
Anthony Calosa
28ec418c76 unused Import 2021-04-30 14:46:49 +00:00
Anthony Calosa
ce56b87b16 Merge branch 'master' into 'master'
minor fixes

See merge request core-developers/forge!4629
2021-04-30 14:37:18 +00:00
Anthony Calosa
6d3155dff9 prevent NPE 2021-04-30 22:27:07 +08:00
Anthony Calosa
9fee1fda1e update order Message 2021-04-30 22:21:56 +08:00
Anthony Calosa
1e1c5fa139 update Filter 2021-04-30 22:20:26 +08:00
Anthony Calosa
70fb1090ca Merge branch 'master' into 'master'
[Android] Android 11+ mandatory use of app-specific directory (obb)

See merge request core-developers/forge!4628
2021-04-30 13:32:57 +00:00
Anthony Calosa
6f13aa4724 [Android] Android 11+ mandatory use of app-specific directory (obb)
- before migration to obb folder, run Forge first and let it download and install necessary files. If Forge is not running (after Android 11 update, white screen bug), disable storage permission and enable storage permission again for Forge. After succesful run, exit Forge then copy your existing Forge folder to Android/obb/forge.app (as suggested by CptKird).
2021-04-30 21:27:46 +08:00
Anthony Calosa
584f923c4c Merge branch 'master' into 'master'
[Android] Enable Sentry

See merge request core-developers/forge!4627
2021-04-30 05:47:31 +00:00
Anthony Calosa
3d1c39da30 [Android] Enable Sentry 2021-04-30 13:45:13 +08:00
Anthony Calosa
a3148c9825 Merge branch 'master' into 'master'
[Mobile] Swap Main Tab and Commander Tab on portrait mode

See merge request core-developers/forge!4626
2021-04-30 05:36:27 +00:00
Anthony Calosa
40e33a43a9 [Mobile] Swap Main Tab and Commander Tab on portrait mode 2021-04-30 13:34:44 +08:00
Michael Kamensky
a3fc74c44e Merge branch 'plague_of_vermin' into 'master'
Add Plague of Vermin

See merge request core-developers/forge!4623
2021-04-29 17:48:39 +00:00
Michael Kamensky
3b28b5fa1c Merge branch 'fixseal' into 'master'
Fix Seal of the Guildpact

See merge request core-developers/forge!4624
2021-04-29 17:47:54 +00:00
tool4EvEr
6070dc9db9 Fix Seal of the Guildpact 2021-04-29 19:41:07 +02:00
Lyu Zong-Hong
7608f02c21 Add Plague of Vermin 2021-04-29 16:10:58 +09:00
Hans Mackowiak
cec5ec0a78 Merge branch 'cards' into 'master'
Fix some Counterspells not exiling when target can't be countered

See merge request core-developers/forge!4616
2021-04-29 06:45:29 +00:00
Michael Kamensky
c72c683915 Merge branch 'update_eureka' into 'master'
Update Eureka and Hypergenesis to put chosen permanent on battlefield sequentially

See merge request core-developers/forge!4622
2021-04-29 05:11:52 +00:00
Lyu Zong-Hong
3384c773da Update Eureka and Hypergenesis to put chosen permanent on battlefield sequentially 2021-04-29 10:51:03 +09:00
Anthony Calosa
8dfc16880e Merge branch 'kevlahnota-master-patch-06695' into 'master'
unused import

See merge request core-developers/forge!4621
2021-04-28 22:01:07 +00:00
Anthony Calosa
5d65861c49 unused import 2021-04-28 22:00:46 +00:00
Hans Mackowiak
179b1419ab Merge branch 'bracers' into 'master'
Battlemage's Bracers: Fix Cost

See merge request core-developers/forge!4615
2021-04-28 21:15:13 +00:00
Hans Mackowiak
4bebc8486c Merge branch 'fixbandingnpe' into 'master'
Fix Banding NPE

See merge request core-developers/forge!4617
2021-04-28 21:14:51 +00:00
Hans Mackowiak
fd0f6e4113 Merge branch 'fixNPE' into 'master'
getTargetableCards: Fix AI NPE

See merge request core-developers/forge!4611
2021-04-28 21:13:19 +00:00
Bug Hunter
8e4c40ba2a getTargetableCards: Fix AI NPE 2021-04-28 21:13:19 +00:00
Hans Mackowiak
abf1a92977 Merge branch 'fixcrash' into 'master'
GameLossAi: Fix crash with Slaughter Pact

See merge request core-developers/forge!4620
2021-04-28 21:12:52 +00:00
Hans Mackowiak
e9ff6ece35 Merge branch 'fixtrig' into 'master'
Fix Karmic Justice

See merge request core-developers/forge!4613
2021-04-28 21:12:08 +00:00
Bug Hunter
ba02ce9261 Fix Karmic Justice 2021-04-28 21:12:07 +00:00
Hans Mackowiak
0ece4427b6 Merge branch 'mana' into 'master'
ManaEffect: Skip producing mana from nothing

See merge request core-developers/forge!4593
2021-04-28 21:00:57 +00:00
Bug Hunter
9afc192154 Revert "Check all RE order quietly"
This reverts commit 0ad6552f8ac900f24f6756bb42973e4b4cbf7a2a.
2021-04-28 21:00:56 +00:00
tool4EvEr
27691d8563 GameLossAi: Fix crash with Slaughter Pact 2021-04-28 21:48:11 +02:00
Anthony Calosa
0ebc6dd2d8 Merge branch 'master' into 'master'
[Desktop] remove unnecessary codes

See merge request core-developers/forge!4619
2021-04-28 18:24:58 +00:00
Anthony Calosa
5a2f57172f [Desktop] remove unnecessary codes 2021-04-29 02:22:26 +08:00
Anthony Calosa
47d30c81b6 Merge branch 'master' into 'master'
[Desktop] update image reader

See merge request core-developers/forge!4618
2021-04-28 17:55:31 +00:00
Anthony Calosa
b80f78d12e [Desktop] update reader 2021-04-29 01:50:37 +08:00
tool4EvEr
835ffaafd4 Fix Banding NPE 2021-04-28 19:28:22 +02:00
tool4EvEr
66f0d580b9 Fix uncounterable 2021-04-28 19:17:04 +02:00
tool4EvEr
524998811c Fix Cost 2021-04-28 18:39:03 +02:00
Anthony Calosa
1b3a8d0cf5 Merge branch 'master' into 'master'
[Desktop] IIOException: Unsupported Image Type

See merge request core-developers/forge!4614
2021-04-28 14:14:16 +00:00
Anthony Calosa
0134d343b2 [Desktop] IIOException: Unsupported Image Type 2021-04-28 22:12:09 +08:00
Michael Kamensky
202ddd8157 Merge branch 'clone' into 'master'
Fix temporary clones keeping Remembered on Self

Closes #1810

See merge request core-developers/forge!4612
2021-04-28 04:09:01 +00:00
Michael Kamensky
76159b2cc4 Merge branch 'undefined' into 'master'
Revised King Goldemar 2 version 2

See merge request core-developers/forge!4594
2021-04-28 04:08:38 +00:00
tool4EvEr
620e95cebf Fix temporary clones keeping Remembered on Self 2021-04-27 21:52:12 +02:00
Michael Kamensky
5f5357d28a Merge branch 'cosmos' into 'master'
Glimpse the Cosmos: Fix AltCost

See merge request core-developers/forge!4604
2021-04-27 19:04:26 +00:00
Michael Kamensky
faee08bb90 Merge branch 'sharesColorWithOther' into 'master'
CardProperty: add SharesColorWithOther to stop comparing cards with themselves

See merge request core-developers/forge!4610
2021-04-27 19:04:20 +00:00
Hans Mackowiak
178f98c82f CardProperty: add SharesColorWithOther to stop comparing cards with themselves 2021-04-27 15:15:08 +02:00
Anthony Calosa
d0dc16aa6f Merge branch 'master' into 'master'
[Android] don't load forge.profile.properties when the user is using the obb directory

See merge request core-developers/forge!4609
2021-04-27 13:07:13 +00:00
Anthony Calosa
77e56e8da9 [Android] don't load forge.profile.properties when the user is using the obb directory
- until google permits unrestricted access outside the app-specific directory on certain apps via flag, the option to use obb as entrypoint is required since accessing outside the scoped storage is really not recommended (https://issuetracker.google.com/issues?q=scoped%20storage%20slow).
2021-04-27 21:04:51 +08:00
Michael Kamensky
045d7bb5ae Merge branch 'fix_grindstone' into 'master'
Fix Grindstone and Sphinx's Tutelage

See merge request core-developers/forge!4608
2021-04-27 12:12:43 +00:00
Lyu Zong-Hong
135f043b60 Fix Grindstone and Sphinx's Tutelage 2021-04-27 18:38:59 +09:00
Hans Mackowiak
682884355f TargetRestrictions: hotfix isDividedAsYouChoose to fix TestCase 2021-04-27 09:17:16 +02:00
Michael Kamensky
702b473453 Merge branch 'fixghired' into 'master'
Fix some cards failing to target for X=0

See merge request core-developers/forge!4601
2021-04-27 04:50:01 +00:00
Anthony Calosa
3f803a3f48 Merge branch 'master' into 'master'
fix divided allocation

See merge request core-developers/forge!4605
2021-04-26 20:01:36 +00:00
Anthony Calosa
742be996fe fix divided allocation 2021-04-27 03:54:45 +08:00
tool4EvEr
228d929a5c Add AffectedZone 2021-04-26 21:05:17 +02:00
tool4EvEr
703fddeb65 Check isDividedAsYouChoose 2021-04-26 18:20:59 +02:00
tool4EvEr
be89397e48 Fix targeting for spells with DividedValue and X=0 2021-04-26 17:44:46 +02:00
Anthony Calosa
2a71716ba4 Merge branch 'master' into 'master'
Net Deck Archive Updates

See merge request core-developers/forge!4603
2021-04-26 09:32:51 +00:00
Churrufli
b8b5561850 Update net-decks-archive-standard.txt 2021-04-26 09:11:54 +00:00
Churrufli
88694f4256 Net Deck Archive Updates 2021-04-26 10:33:45 +02:00
Michael Kamensky
4d7126d3c3 Merge branch 'growth' into 'master'
AI & Exponential Growth: Fix illegal target

See merge request core-developers/forge!4599
2021-04-26 07:20:13 +00:00
Anthony Calosa
87ab6c1de2 Merge branch 'master' into 'master'
[Mobile-Dev] Fix alt-tab when fullscreen

See merge request core-developers/forge!4602
2021-04-26 02:35:58 +00:00
Anthony Calosa
e649c9128e [Mobile-Dev] Fix alt-tab when fullscreen 2021-04-26 10:34:44 +08:00
tool4EvEr
9b774734c3 Fix some cards failing to target for X=0 2021-04-25 22:47:54 +02:00
tool4EvEr
841164bafb Fix illegal target 2021-04-25 21:28:13 +02:00
Michael Kamensky
bc61ec0495 Merge branch 'kozilek' into 'master'
Fix Kozilek, the Great Distortion

See merge request core-developers/forge!4598
2021-04-25 10:36:06 +00:00
tool4EvEr
c37bc213e5 Fix Kozilek, the Great Distortion 2021-04-25 11:49:09 +02:00
Michael Kamensky
080639d2d9 Merge branch 'shusher' into 'master'
Fix Vexing Shusher

See merge request core-developers/forge!4597
2021-04-25 09:10:33 +00:00
Michael Kamensky
ab2d0771ab Merge branch 'punch' into 'master'
Gravitic Punch: Add DamageSource

See merge request core-developers/forge!4596
2021-04-25 09:10:21 +00:00
tool4EvEr
204d502c24 Fix Vexing Shusher 2021-04-25 10:59:36 +02:00
tool4EvEr
985b60a565 Add DamageSource 2021-04-25 10:44:19 +02:00
Michael Kamensky
8202c4abe3 Merge branch 'fixstack' into 'master'
removeInstancesControlledBy - also filter simultaneousStackEntryList

See merge request core-developers/forge!4587
2021-04-25 05:45:44 +00:00
Michael Kamensky
4de73cebe0 Merge branch 'AImdfc' into 'master'
AI: basic support playing MDFC lands

See merge request core-developers/forge!4589
2021-04-25 05:45:34 +00:00
Anthony Calosa
9bb6e833fb Merge branch 'master' into 'master'
refactor getStartingPlaneswalkerOptions

See merge request core-developers/forge!4595
2021-04-25 03:51:39 +00:00
Anthony Calosa
d227da0c10 refactor getStartingPlaneswalkerOptions 2021-04-25 11:49:05 +08:00
tool4EvEr
962330d491 Fix ETB 2021-04-25 00:00:09 +02:00
Tim McFadden
c5d9a98549 Upload New File 2021-04-24 11:09:04 +00:00
Michael Kamensky
9b83c78346 Merge branch 'conquest' into 'master'
Allow cards from precon products in Planar Conquest

See merge request core-developers/forge!4573
2021-04-24 07:36:12 +00:00
Michael Kamensky
8a7d0d4242 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2021-04-24 10:23:45 +03:00
Michael Kamensky
45440adc01 Merge branch 'master' into 'master'
Prepare Forge for Android publish 1.6.40.001 [incremental].

See merge request core-developers/forge!4592
2021-04-24 07:22:29 +00:00
Michael Kamensky
8feaf9ed3e [maven-release-plugin] prepare for next development iteration 2021-04-24 08:26:10 +03:00
Michael Kamensky
636020d870 [maven-release-plugin] prepare release forge-1.6.40 2021-04-24 08:25:57 +03:00
Michael Kamensky
3150aa2e7c - Prepare Forge for Android publish 1.6.40.001 [incremental]. 2021-04-24 08:13:13 +03:00
swordshine
ce22449f41 Merge branch 'shadrix' into 'master'
Shadrix Silverquill: placer is target

See merge request core-developers/forge!4588
2021-04-24 03:04:02 +00:00
swordshine
1c938b5d29 Merge branch 'arix' into 'master'
Arixmethes: always remove counter

See merge request core-developers/forge!4590
2021-04-24 03:03:39 +00:00
swordshine
0493104beb Merge branch 'master' into 'master'
update translations

See merge request core-developers/forge!4591
2021-04-24 03:02:56 +00:00
CCTV-1
9786d9f927 update cmc translation,add missing translation. 2021-04-24 10:39:37 +08:00
CCTV-1
73e5254e2b add missing string 2021-04-24 10:38:51 +08:00
tool4EvEr
7c934d29ac always remove counter 2021-04-23 23:41:24 +02:00
tool4EvEr
bccbf3e55e Add more logic 2021-04-23 23:04:05 +02:00
tool4EvEr
8196ee531a AI: basic support playing MDFC lands 2021-04-23 22:43:14 +02:00
tool4EvEr
39729cb3da placer is target 2021-04-23 22:00:40 +02:00
tool4EvEr
cdc1f6014e also filter simultaneousStackEntryList 2021-04-23 21:25:58 +02:00
Michael Kamensky
aadfdac4c5 Merge branch 'Williams-master-patch-36117' into 'master'
SLD Dr Lair Secretorium Super Drop

See merge request core-developers/forge!4583
2021-04-23 19:17:23 +00:00
John
2e358116c2 Update Secret Lair Drop Series.txt 2021-04-23 14:50:59 +00:00
swordshine
71d88523b2 Merge branch 'update_japanese_localization_mana_value' into 'master'
Update Mana Value in Japanese Localization

See merge request core-developers/forge!4582
2021-04-23 10:12:14 +00:00
swordshine
65e702aed7 Merge branch 'patch' into 'master'
More cleanup

See merge request core-developers/forge!4585
2021-04-23 10:07:58 +00:00
sowrdshine
99bb45d96a More cleanup 2021-04-23 18:07:04 +08:00
swordshine
4f41be641a Merge branch 'patch' into 'master'
Fixed Leyline Invocation

See merge request core-developers/forge!4584
2021-04-23 09:18:45 +00:00
sowrdshine
5ab030242b Fixed Leyline Invocation 2021-04-23 17:17:34 +08:00
John
736d9bbd98 Update Secret Lair Drop Series.txt 2021-04-23 08:23:48 +00:00
Lyu Zong-Hong
6295ebdb09 Update Mana Value in Japanese Localization 2021-04-23 13:35:44 +09:00
Michael Kamensky
380a27b83d Merge branch 'update_in_game_text' into 'master'
Update in-game text and also English and Japanese localization (CMC -> mana value and etc.)

See merge request core-developers/forge!4581
2021-04-23 04:30:06 +00:00
Michael Kamensky
691af037a4 Merge branch 'phoenix' into 'master'
Retriever Phoenix: Fix check

See merge request core-developers/forge!4577
2021-04-23 04:29:03 +00:00
Michael Kamensky
70cb78a67d Merge branch 'scrollmenu' into 'master'
Support scrolling when abilities Popup has too many entries

See merge request core-developers/forge!4567
2021-04-23 04:28:50 +00:00
Michael Kamensky
4bd5fe0178 Merge branch 'changetargetsai' into 'master'
ChangeTargetsAI: Improve decision making

See merge request core-developers/forge!4569
2021-04-23 04:27:57 +00:00
Michael Kamensky
babd80edd9 Merge branch 'update_oracle_stx' into 'master'
Update Oracle after STX release

See merge request core-developers/forge!4564
2021-04-23 04:27:30 +00:00
Alumi
74e4efba52 Update Oracle after STX release 2021-04-23 04:27:27 +00:00
Lyu Zong-Hong
2d7e88a8c2 Update in-game text and also English and Japanese localization 2021-04-23 11:01:34 +09:00
Anthony Calosa
d82c8d466a Merge branch 'master' into 'master'
update check

See merge request core-developers/forge!4580
2021-04-23 01:15:12 +00:00
Sol
9edbbf399f Merge branch 'esix' into 'master'
Esix: Script typo

See merge request core-developers/forge!4575
2021-04-23 01:13:11 +00:00
Anthony Calosa
345316b9b0 update check 2021-04-23 09:12:02 +08:00
Anthony Calosa
0bb6636613 Merge branch 'master' into 'master'
[Android] add option to use obb directory as assets directory for Forge

See merge request core-developers/forge!4579
2021-04-22 23:14:25 +00:00
Anthony Calosa
1929b75e34 [Android] add option to use obb directory as assets directory for Forge 2021-04-23 06:45:35 +08:00
tool4EvEr
245827825e Fix check 2021-04-22 20:41:36 +02:00
Bug Hunter
fc9f81c5c9 Merge branch 'fixplaymain1' into 'master'
Fix PlayMain1 scripts

See merge request core-developers/forge!4576
2021-04-22 18:15:13 +00:00
tool4EvEr
987dce2643 Fix PlayMain1 scripts 2021-04-22 20:14:31 +02:00
tool4EvEr
4f2abad39d Script typo 2021-04-22 20:03:19 +02:00
tool4EvEr
354000082b Clean up 2021-04-22 18:50:06 +02:00
Michael Kamensky
b8e87604c2 Merge branch 'master' into 'master'
Achievements for STX and C21 by Marek.

See merge request core-developers/forge!4574
2021-04-22 16:36:16 +00:00
Michael Kamensky
49f7ad1281 - Achievements for STX and C21 by Marek. 2021-04-22 19:35:37 +03:00
Michael Kamensky
6731cdcc9d Merge branch 'twosat-master-patch-53531' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4571
2021-04-22 16:25:21 +00:00
Michael Kamensky
5306510ec1 Merge branch 'nacatl' into 'master'
Nacatl War-Pride and support

See merge request core-developers/forge!4572
2021-04-22 16:25:15 +00:00
Northmoc
e8c0655186 allow precon cards in Planar Conquest 2021-04-22 12:18:42 -04:00
Northmoc
18d0bf1c7b Add support for keyword: CARDNAME must be blocked by exactly one creature if able. 2021-04-22 12:00:18 -04:00
Northmoc
0b9755405e nacatl_war_pride.txt 2021-04-22 11:58:36 -04:00
Andreas Bendel
8b7a183e63 Update de-DE.properties
CMC is now Mana Value
2021-04-22 15:20:52 +00:00
Tim Mocny
7a094478f3 Merge branch 'typo' into 'master'
teachings_of_the_archaics.txt only 3 cards

See merge request core-developers/forge!4570
2021-04-22 14:07:47 +00:00
Michael Kamensky
bd063c71f8 Merge branch 'fixNPE' into 'master'
Rework ceaseToExist

See merge request core-developers/forge!4559
2021-04-22 11:25:36 +00:00
Michael Kamensky
693ef6c4d3 Merge branch 'countersMove' into 'master'
CountersMoveAI: Improve logic for Ozolith

See merge request core-developers/forge!4568
2021-04-22 05:03:54 +00:00
tool4EvEr
60a02cd2a5 Improve decision making for multiplayer 2021-04-22 00:08:19 +02:00
tool4EvEr
e25c7dac4a Improve logic for Ozolith 2021-04-21 22:32:37 +02:00
tool4EvEr
d918cade67 Hold triggers until all cards are processed 2021-04-21 21:07:47 +02:00
tool4EvEr
982400b74d Clean up 2021-04-21 20:50:13 +02:00
tool4EvEr
c00a384591 Add MouseWheel support 2021-04-21 19:55:14 +02:00
Michael Kamensky
b0e1630fc5 Merge branch 'tokens' into 'master'
update some token lists

See merge request core-developers/forge!4566
2021-04-21 17:52:24 +00:00
tool4EvEr
50ffd7b4a9 Support scrolling when Popup has too many entries 2021-04-21 19:37:05 +02:00
Northmoc
e4e75a5f73 update some token lists 2021-04-21 13:18:27 -04:00
Northmoc
7da4381d38 update some token lists 2021-04-21 13:18:27 -04:00
Hans Mackowiak
ef8076b626 GameEntityCounterTable: fix NPE and filterToRemove 2021-04-21 17:35:41 +02:00
Bug Hunter
ab5f5c1ce9 Merge branch 'yarok' into 'master'
Yarok: Fix staticability check

See merge request core-developers/forge!4565
2021-04-21 15:26:05 +00:00
tool4EvEr
6d442b12a0 Yarok: Fix staticability check 2021-04-21 17:25:42 +02:00
Sol
0019f83e33 Merge branch 'breena' into 'master'
Fix Breena Trigger

See merge request core-developers/forge!4560
2021-04-21 14:13:20 +00:00
Hans Mackowiak
f3011b1790 Merge branch 'fixMana' into 'master'
Fix mana conversion checking wrong card from hand

See merge request core-developers/forge!4561
2021-04-21 04:36:17 +00:00
Hans Mackowiak
ea79f84b71 Merge branch 'fixyarok' into 'master'
Fix Yarok

See merge request core-developers/forge!4562
2021-04-21 04:35:53 +00:00
tool4EvEr
040d6929dc Fix Yarok 2021-04-20 23:15:32 +02:00
tool4EvEr
2fe9c38fff Fix mana conversion checking wrong card from hand 2021-04-20 20:21:39 +02:00
tool4EvEr
256b54b6b5 Fix Breena Trigger 2021-04-20 19:01:57 +02:00
Michael Kamensky
062ad5d92f Merge branch 'esix_fractal_bloom' into 'master'
Add Esix, Fractal Bloom and necessary supports

See merge request core-developers/forge!4557
2021-04-20 16:44:22 +00:00
Michael Kamensky
5806ef248f Merge branch 'ee' into 'master'
Last STX card! elemental_expressionist.txt

See merge request core-developers/forge!4554
2021-04-20 16:44:04 +00:00
Michael Kamensky
257fcf1b3d Merge branch 'elspeth' into 'master'
AI: Fix Elspeth Conquers Death

See merge request core-developers/forge!4552
2021-04-20 16:43:47 +00:00
tool4EvEr
284db016a0 Rework ceaseToExist 2021-04-20 18:28:01 +02:00
tool4EvEr
5e2898f0ae Improve Counter check 2021-04-20 17:29:20 +02:00
Northmoc
207408ae20 elemental_expressionist.txt 2021-04-20 08:59:54 -04:00
Bug Hunter
2843ca9b49 Merge branch 'TRT-master-patch-20042' into 'master'
Update forge-gui/res/cardsfolder/upcoming/detention_vortex.txt

See merge request core-developers/forge!4558
2021-04-20 12:05:08 +00:00
Bug Hunter
efc09d8a3f Update forge-gui/res/cardsfolder/upcoming/detention_vortex.txt 2021-04-20 12:03:43 +00:00
Lyu Zong-Hong
e6714bf45e Add Esix, Fractal Bloom and necessary supports 2021-04-20 20:47:22 +09:00
Michael Kamensky
8049e2c322 Merge branch 'update_japanese_translation' into 'master'
Update Japanese Card Translation

See merge request core-developers/forge!4556
2021-04-20 11:45:46 +00:00
Lyu Zong-Hong
fdb0db2d41 Update Japanese Card Translation 2021-04-20 19:54:12 +09:00
Michael Kamensky
7465031d44 Merge branch 'fixTriggerCardState' into 'master'
TriggerHandler: fix setting CardState for Trigger

See merge request core-developers/forge!4555
2021-04-20 07:17:09 +00:00
Hans Mackowiak
15eb3a421d TriggerHandler: fix setting CardState for Trigger 2021-04-20 08:45:48 +02:00
Michael Kamensky
ec52a07fe9 Merge branch 'clone' into 'master'
Temporary clones: restore Remembered/Imprinted

Closes #1792

See merge request core-developers/forge!4490
2021-04-20 04:00:42 +00:00
Bug Hunter
b7a475cc0a Temporary clones: restore Remembered/Imprinted 2021-04-20 04:00:41 +00:00
Michael Kamensky
aed022d099 Merge branch 'master' into 'master'
[Mobile] Update LibGDX to 1.10.0, Update Mobile Dev to LWJGL3

See merge request core-developers/forge!4548
2021-04-20 03:59:15 +00:00
Michael Kamensky
86b37e7df0 Merge branch 'chaos' into 'master'
Chaos Warp: Fix wrong target

See merge request core-developers/forge!4551
2021-04-20 03:58:15 +00:00
Michael Kamensky
09e9b46efd Merge branch 'cleanup' into 'master'
Fixes - 19 Apr

See merge request core-developers/forge!4553
2021-04-20 03:58:06 +00:00
Hans Mackowiak
90b685c15d Merge branch 'causeRemoval' into 'master'
remove AbilityUtils.getCause

See merge request core-developers/forge!4550
2021-04-20 00:01:50 +00:00
Northmoc
daedffc0da specter_of_the_fens.txt add Flying! 2021-04-19 15:53:25 -04:00
Northmoc
c489f88f77 brainstorm.txt cleanup Descs 2021-04-19 15:50:55 -04:00
tool4EvEr
33779bb6cf AI: Fix Elspeth Conquers Death 2021-04-19 19:56:13 +02:00
tool4EvEr
660806fb7c Fix wrong target 2021-04-19 19:31:46 +02:00
Hans Mackowiak
40a52ed00b remove AbilityUtils.getCause 2021-04-19 18:12:16 +02:00
Michael Kamensky
c216498198 Merge branch 'master' into 'master'
Update cards translations

See merge request core-developers/forge!4540
2021-04-19 13:52:31 +00:00
Michael Kamensky
547c887c0b Merge branch 'master' into 'master'
Add a NPE guard to ChooseGenericEffectAi

See merge request core-developers/forge!4549
2021-04-19 13:51:41 +00:00
Michael Kamensky
dfe23da44d - Add a NPE guard to ChooseGenericEffectAi 2021-04-19 16:51:00 +03:00
Anthony Calosa
a5e8b88e32 [Mobile] Update LibGDX to 1.10.0, Update Mobile Dev to LWJGL3
refer here for LibGDX Migration/Changes: https://libgdx.com/news/2021/04/the-ultimate-migration-guide
2021-04-19 21:41:34 +08:00
Hans Mackowiak
539c94f36f fix descendants path 2021-04-19 15:02:40 +02:00
Churrufli
fce5ae0498 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2021-04-19 13:09:01 +02:00
Hans Mackowiak
8fbf96098e CountersPutAi: fix NPE in Polukranos 2021-04-19 13:08:19 +02:00
Hans Mackowiak
c8b43fa8ab Kwain: fix Ability Description 2021-04-19 13:08:19 +02:00
Michael Kamensky
9f305347a8 Merge branch 'PolukranosLogicFix' into 'master'
CountersPutAi: fix NPE in Polukranos

See merge request core-developers/forge!4547
2021-04-19 10:37:47 +00:00
Hans Mackowiak
94d826d2a4 CountersPutAi: fix NPE in Polukranos 2021-04-19 10:37:23 +00:00
Hans Mackowiak
118cf70e40 Kwain: fix Ability Description 2021-04-19 10:21:43 +02:00
Bug Hunter
895d8d594b Bishop of Binding: Update script 2021-04-19 09:07:44 +02:00
tool4EvEr
60ce467859 Fix wrong activator 2021-04-19 09:07:44 +02:00
tool4EvEr
6efa58006e Fix getReflectableManaColors 2021-04-19 09:07:43 +02:00
paul_snoops
f253019daf Brawl STX update 2021-04-19 09:07:43 +02:00
Alumi
8600aceb64 Fix STX typos in edition file 2021-04-19 09:07:43 +02:00
tool4EvEr
a358af534a Improve logic for preventing loops 2021-04-19 09:07:43 +02:00
Anthony Calosa
d080676997 Update mila_crafty_companion_lukka_wayward_bonder.txt 2021-04-19 09:07:43 +02:00
Anthony Calosa
b3412fcfb4 fix lukka wayward bonder ability 2021-04-19 09:07:43 +02:00
Adam Pantel
b5e3db279d Mill quick fix 2021-04-19 09:07:43 +02:00
Adam Pantel
a29ca46ca3 Add ChangesZoneAll trigger in GameAction 2021-04-19 09:07:43 +02:00
Adam Pantel
2fe54a28b1 Add TriggerZones 2021-04-19 09:07:43 +02:00
Adam Pantel
6f9bc11078 Costs should trigger Ranar/Laelia 2021-04-19 09:07:43 +02:00
Northmoc
cd5186cd0d jesters_cap.txt + Mandatory 2021-04-19 09:07:43 +02:00
Northmoc
34cdc299b7 + Mandatory to some ChangeZone effects 2021-04-19 09:07:42 +02:00
Northmoc
274a3f78f2 rampaging_cyclops.txt CheckSVar -> IsPresent 2021-04-19 09:07:42 +02:00
Northmoc
0e31ad96a5 coralhelm_commander.txt + PresentCompare 2021-04-19 09:07:42 +02:00
Northmoc
b191e4d83a beastbreaker_of_bala_ged.txt CheckSVar -> IsPresent 2021-04-19 09:07:42 +02:00
Northmoc
16c554aa43 angelic_field_marshal.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:42 +02:00
Northmoc
80550cd42e battle_brawler.txt CheckSVar -> IsPresent 2021-04-19 09:07:42 +02:00
Northmoc
3ac28aa279 desperate_castaways.txt CheckSVar -> IsPresent 2021-04-19 09:07:42 +02:00
Northmoc
f16ea99cbc angelic_voices.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:42 +02:00
Northmoc
d4353bd237 woodborn_behemoth.txt CheckSVar -> IsPresent 2021-04-19 09:07:42 +02:00
Northmoc
8efa82abc2 drover_of_the_mighty.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:42 +02:00
Northmoc
a5f42f77ad aeronaut_tinkerer.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:42 +02:00
Northmoc
384314d9ab gate_hound.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:41 +02:00
Northmoc
56d63a46bf angelic_overseer.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:41 +02:00
Northmoc
da00ccff58 court_homunculus.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:41 +02:00
Northmoc
313d5ab762 dauntless_dourbark.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:41 +02:00
Northmoc
a569029c40 coralhelm_commander.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:41 +02:00
Northmoc
04748cf03f abzan_kin_guard.txt CheckSVar -> IsPresent 2021-04-19 09:07:41 +02:00
Northmoc
91871fb96d ember_weaver.txt CheckSVar -> IsPresent 2021-04-19 09:07:41 +02:00
Northmoc
7292a12ff4 nightfire_giant.txt CheckSVar -> IsPresent 2021-04-19 09:07:41 +02:00
Northmoc
9b04e8ff38 cliffrunner_behemoth.txt CheckSVar -> IsPresent 2021-04-19 09:07:41 +02:00
Northmoc
b85a2ea6d2 pterodon_knight.txt CheckSVar -> IsPresent 2021-04-19 09:07:41 +02:00
Northmoc
da6c134540 kondas_hatamoto.txt CheckSVar -> IsPresent 2021-04-19 09:07:40 +02:00
Northmoc
e09da1a600 jund_hackblade.txt CheckSVar -> IsPresent 2021-04-19 09:07:40 +02:00
Northmoc
5aa1f9863d ashenmoor_cohort.txt CheckSVar -> IsPresent 2021-04-19 09:07:40 +02:00
Northmoc
1eeb3637e8 villainous_ogre.txt CheckSVar -> IsPresent, AI 2021-04-19 09:07:40 +02:00
Northmoc
79e229467e ereboss_titan.txt CheckSVar -> IsPresent 2021-04-19 09:07:40 +02:00
Northmoc
d483ea7f47 - add SelectPrompt 2021-04-19 09:07:40 +02:00
Northmoc
4e4b31a0d5 bond_of_insight.txt clean up StackDesc and AI 2021-04-19 09:07:40 +02:00
Northmoc
eb38c1ec15 shadrix_silverquill.txt improve TgtPrompts for modes 2021-04-19 09:07:40 +02:00
Northmoc
b33406da4d garrison_sergeant.txt use IsPresent, improve AI 2021-04-19 09:07:40 +02:00
Northmoc
10f4da71b8 gravecrawler.txt fix static and typos, add AI 2021-04-19 09:07:40 +02:00
Michael Kamensky
6d27a8e489 Merge branch 'bishop' into 'master'
Bishop of Binding: Update script

See merge request core-developers/forge!4544
2021-04-19 04:22:11 +00:00
Bug Hunter
c63477c34e Bishop of Binding: Update script 2021-04-19 04:22:10 +00:00
Michael Kamensky
761d1877fa Merge branch 'fixmana' into 'master'
Fix getReflectableManaColors

See merge request core-developers/forge!4537
2021-04-18 14:11:59 +00:00
tool4EvEr
cf4dbf6ed9 Fix wrong activator 2021-04-18 14:11:46 +00:00
tool4EvEr
53e2df8aa2 Fix getReflectableManaColors 2021-04-18 14:11:46 +00:00
Michael Kamensky
07278de968 Merge branch 'brawl' into 'master'
Brawl STX update

See merge request core-developers/forge!4543
2021-04-18 14:11:28 +00:00
paul_snoops
6710c47db0 Brawl STX update 2021-04-18 14:11:17 +00:00
Michael Kamensky
a5cc820ea6 Merge branch 'fix_stx_typos' into 'master'
Fix STX typos in edition file

See merge request core-developers/forge!4541
2021-04-18 14:10:02 +00:00
Alumi
5eb95980bf Fix STX typos in edition file 2021-04-18 14:10:02 +00:00
Michael Kamensky
57cc0e920a Merge branch 'cloneAI' into 'master'
CloneAI: Improve logic for preventing loops

See merge request core-developers/forge!4545
2021-04-18 14:09:12 +00:00
tool4EvEr
902b707b5e Improve logic for preventing loops 2021-04-18 15:32:50 +02:00
Anthony Calosa
98600e9a19 Merge branch 'kevlahnota-master-patch-80770' into 'master'
fix lukka wayward bonder ability

See merge request core-developers/forge!4542
2021-04-18 06:43:21 +00:00
Anthony Calosa
fbf8ac4883 Update mila_crafty_companion_lukka_wayward_bonder.txt 2021-04-18 06:41:45 +00:00
Anthony Calosa
4c7a25407a fix lukka wayward bonder ability 2021-04-18 06:24:02 +00:00
Michael Kamensky
af9359850d Merge branch 'ranar-2' into 'master'
Some Ranar/Laelia fixes

See merge request core-developers/forge!4538
2021-04-18 04:19:44 +00:00
Adam Pantel
abe5638af5 Mill quick fix 2021-04-18 04:19:32 +00:00
Adam Pantel
c45e8a11d7 Add ChangesZoneAll trigger in GameAction 2021-04-18 04:19:32 +00:00
Adam Pantel
62ef84ea07 Add TriggerZones 2021-04-18 04:19:32 +00:00
Adam Pantel
17b5098602 Costs should trigger Ranar/Laelia 2021-04-18 04:19:32 +00:00
Michael Kamensky
272a7afe9e Merge branch 'fix' into 'master'
Fixes - 4/17

See merge request core-developers/forge!4539
2021-04-18 04:17:57 +00:00
Northmoc
9c7fa1a6a8 jesters_cap.txt + Mandatory 2021-04-17 19:34:26 -04:00
Northmoc
38a050dd13 + Mandatory to some ChangeZone effects 2021-04-17 19:32:18 -04:00
Northmoc
377396d77c rampaging_cyclops.txt CheckSVar -> IsPresent 2021-04-17 19:26:39 -04:00
Northmoc
80fc8eddb9 coralhelm_commander.txt + PresentCompare 2021-04-17 19:25:01 -04:00
Northmoc
d9c23ece15 beastbreaker_of_bala_ged.txt CheckSVar -> IsPresent 2021-04-17 19:23:26 -04:00
Northmoc
84a913451f angelic_field_marshal.txt CheckSVar -> IsPresent, AI 2021-04-17 19:19:41 -04:00
Northmoc
59794efc95 battle_brawler.txt CheckSVar -> IsPresent 2021-04-17 19:15:38 -04:00
Northmoc
13977a07e5 desperate_castaways.txt CheckSVar -> IsPresent 2021-04-17 19:15:37 -04:00
Northmoc
b1eb8bd8ec angelic_voices.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:37 -04:00
Northmoc
69043cfd1c woodborn_behemoth.txt CheckSVar -> IsPresent 2021-04-17 19:15:36 -04:00
Northmoc
f34c713e7e drover_of_the_mighty.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:36 -04:00
Northmoc
e180bf21a7 aeronaut_tinkerer.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:35 -04:00
Northmoc
fdaaa4be30 gate_hound.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:35 -04:00
Northmoc
4db445dd3e angelic_overseer.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:34 -04:00
Northmoc
530d4c0b08 court_homunculus.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:34 -04:00
Northmoc
100ce6ded9 dauntless_dourbark.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:33 -04:00
Northmoc
03deb78aab coralhelm_commander.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:33 -04:00
Northmoc
bb220366be abzan_kin_guard.txt CheckSVar -> IsPresent 2021-04-17 19:15:32 -04:00
Northmoc
cd4e4a0892 ember_weaver.txt CheckSVar -> IsPresent 2021-04-17 19:15:32 -04:00
Northmoc
aa42c302aa nightfire_giant.txt CheckSVar -> IsPresent 2021-04-17 19:15:31 -04:00
Northmoc
5128557e4e cliffrunner_behemoth.txt CheckSVar -> IsPresent 2021-04-17 19:15:31 -04:00
Northmoc
a181f5d1db pterodon_knight.txt CheckSVar -> IsPresent 2021-04-17 19:15:31 -04:00
Northmoc
5e3bc6c507 kondas_hatamoto.txt CheckSVar -> IsPresent 2021-04-17 19:15:30 -04:00
Northmoc
b11a9557d7 jund_hackblade.txt CheckSVar -> IsPresent 2021-04-17 19:15:30 -04:00
Northmoc
593e172832 ashenmoor_cohort.txt CheckSVar -> IsPresent 2021-04-17 19:15:29 -04:00
Northmoc
6b62a7fde3 villainous_ogre.txt CheckSVar -> IsPresent, AI 2021-04-17 19:15:29 -04:00
Northmoc
9a80028ec7 ereboss_titan.txt CheckSVar -> IsPresent 2021-04-17 19:15:28 -04:00
Northmoc
1252e22743 - add SelectPrompt 2021-04-17 19:15:28 -04:00
Northmoc
a3a25d9bef bond_of_insight.txt clean up StackDesc and AI 2021-04-17 19:15:27 -04:00
Northmoc
08fa3a5768 shadrix_silverquill.txt improve TgtPrompts for modes 2021-04-17 19:15:27 -04:00
Northmoc
52cd521902 garrison_sergeant.txt use IsPresent, improve AI 2021-04-17 19:15:26 -04:00
Northmoc
6dd5f1991f gravecrawler.txt fix static and typos, add AI 2021-04-17 19:15:26 -04:00
Churrufli
d3d29e03be Update cards translations 2021-04-18 00:07:34 +02:00
Michael Kamensky
2ddc88dd87 Merge branch 'mudfix' into 'master'
Mudslide cleanup

See merge request core-developers/forge!4535
2021-04-17 14:46:22 +00:00
tool4EvEr
f768aa360f Mudslide cleanup 2021-04-17 14:46:07 +00:00
Bug Hunter
5ba12d009b Merge branch 'ringtypo' into 'master'
Replicating Ring: Fix typo

See merge request core-developers/forge!4536
2021-04-17 10:59:02 +00:00
tool4EvEr
6259891312 Replicating Ring: Fix typo 2021-04-17 12:58:14 +02:00
Michael Kamensky
fad59e4a0f Merge branch 'fix' into 'master'
Fixes - 16 Apr

See merge request core-developers/forge!4532
2021-04-17 04:23:48 +00:00
Michael Kamensky
81852fe8c6 Merge branch 'ranar-2' into 'master'
Ranar v2

See merge request core-developers/forge!4534
2021-04-17 04:22:26 +00:00
Adam Pantel
b33e262de6 Ranar v2 2021-04-17 04:22:25 +00:00
Northmoc
7963919b72 dungeon_master.txt rogue "" 2021-04-16 18:48:59 -04:00
Northmoc
e7f87478b4 fix SpellDesc 2021-04-16 18:00:51 -04:00
Northmoc
c6b8d95747 ruthless_winnower.txt tidy up 2021-04-16 17:22:35 -04:00
Anthony Calosa
8468c5ac52 Merge branch 'master' into 'master'
refactor decompression

See merge request core-developers/forge!4533
2021-04-16 19:42:50 +00:00
Northmoc
27d7ae4b78 allow Coalition Honor Guard in MB1 boosters 2021-04-16 15:38:25 -04:00
Anthony Calosa
fffd2afe5e refactor decompression 2021-04-17 03:27:46 +08:00
Anthony Calosa
15ba670d5a Merge remote-tracking branch 'remotes/core/master' 2021-04-17 00:49:09 +08:00
Northmoc
7e7279a642 wandering_archaic_explore_the_vastlands.txt add Controller$ You to CopySpellAbility 2021-04-16 10:34:56 -04:00
Northmoc
1b765e2408 sky_swallower.txt fix GainControl line 2021-04-16 10:32:25 -04:00
Northmoc
db0ae9253e mage_duel.txt remove bad Cost 2021-04-16 10:28:47 -04:00
Bug Hunter
5573218794 Merge branch 'Northmoc-master-patch-13728' into 'master'
Update lorehold_pledgemage.txt

See merge request core-developers/forge!4531
2021-04-16 14:27:55 +00:00
Northmoc
6e38f16c83 mentors_guidance.txt remove bad Cost 2021-04-16 10:26:45 -04:00
Northmoc
4f0d2370ac bold_plagiarist.txt TriggerZone 2021-04-16 10:26:44 -04:00
Tim Mocny
f49f677107 Update lorehold_pledgemage.txt 2021-04-16 14:18:53 +00:00
Michael Kamensky
cbb11ea723 Merge branch 'kardur' into 'master'
Remove Goad from Kardur

See merge request core-developers/forge!4527
2021-04-16 12:44:41 +00:00
Michael Kamensky
566d011e2e Merge branch 'Williams-master-patch-46381' into 'master'
C21 Tokens

See merge request core-developers/forge!4529
2021-04-16 12:43:34 +00:00
Michael Kamensky
1d2a62d06c Merge branch 'svarsStackInstance' into 'master'
StackInstance only store direct SVars

See merge request core-developers/forge!4528
2021-04-16 12:43:19 +00:00
Hans Mackowiak
743163fce7 StackInstance only store direct SVars 2021-04-16 12:43:19 +00:00
Michael Kamensky
afe5333c88 Merge branch 'filter_more_targetable_cards' into 'master'
Filter more cards in getTargetableCards

See merge request core-developers/forge!4513
2021-04-16 12:41:10 +00:00
John
3bf15b5a35 Update Commander 2021.txt 2021-04-16 08:10:23 +00:00
Hans Mackowiak
1c9cfbc4bb Merge branch 'card-fixes' into 'master'
Fix Zurzoth, Akiri

See merge request core-developers/forge!4519
2021-04-16 07:15:29 +00:00
Adam Pantel
65093b0388 Fix Zurzoth, Akiri 2021-04-16 07:15:29 +00:00
Anthony Calosa
e2320ea77c Merge remote-tracking branch 'remotes/core/master' 2021-04-16 13:30:25 +08:00
Michael Kamensky
59f414f707 Merge branch 'fixmp' into 'master'
Fix Multiplayer LTB triggers

Closes #1324

See merge request core-developers/forge!4511
2021-04-16 04:59:35 +00:00
Michael Kamensky
2a2c12cf77 Merge branch 'titan' into 'master'
ChangeZoneTable for Triplicate Titan

See merge request core-developers/forge!4522
2021-04-16 04:58:41 +00:00
Michael Kamensky
5fd34dc967 Merge branch 'twosat-master-patch-34511' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4523
2021-04-16 04:58:26 +00:00
Michael Kamensky
65df6765da Merge branch 'shamans_trance' into 'master'
Add Shaman's Trance

See merge request core-developers/forge!4526
2021-04-16 04:56:57 +00:00
Northmoc
ff9729d27a CombatUtil.java add "quasi-goad" logic 2021-04-15 20:03:44 -04:00
Northmoc
9876741c50 kardur_doomscourge.txt remove Goad effect 2021-04-15 20:02:44 -04:00
Lyu Zong-Hong
a1f69f0732 Add Shaman's Trance 2021-04-16 08:22:37 +09:00
Andreas Bendel
8174b883a0 Update de-DE.properties
added line and translation lblRandomHistoricArchetypeDecks
translated lblInvalidTargetSpecification
2021-04-15 19:38:32 +00:00
tool4EvEr
337065271e ChangeZoneTable for Triplicate Titan 2021-04-15 21:27:31 +02:00
Anthony Calosa
01dd0e7fc4 Merge remote-tracking branch 'remotes/core/master' 2021-04-16 03:15:02 +08:00
Anthony Calosa
f3ee08ab9a Merge branch 'master' into 'master'
add tokenscript

Closes #1803

See merge request core-developers/forge!4521
2021-04-15 19:09:34 +00:00
Anthony Calosa
ba08c09607 fix triplicate titan 2021-04-15 19:09:29 +00:00
Anthony Calosa
aecfd12911 remove accidental commit 2021-04-16 03:07:54 +08:00
Anthony Calosa
c081e48467 fix triplicate titan 2021-04-16 03:05:40 +08:00
tool4EvEr
237b0324c1 Clean up 2021-04-15 20:26:28 +02:00
tool4EvEr
5aaf39f40b LTB triggers for opponents 2021-04-15 20:20:52 +02:00
austinio7116
7a4ed29945 Merge branch 'historicsanctionfix' into 'master'
Historic GA fix

See merge request core-developers/forge!4520
2021-04-15 17:59:59 +00:00
austinio7116
84014ce9b5 Historic GA fix 2021-04-15 18:56:25 +01:00
austinio7116
08c8e84d72 Merge branch 'ldabuildonly2' into 'master'
LDA module build fixes

See merge request core-developers/forge!4476
2021-04-15 16:55:40 +00:00
tool4EvEr
43171879a0 Fix logic 2021-04-15 18:54:58 +02:00
tool4EvEr
2f7a30f920 Fix Multiplayer LTB triggers 2021-04-15 18:54:57 +02:00
Anthony Calosa
27a93b318d Merge branch 'master' into 'master'
update zip decompression

Closes #1802

See merge request core-developers/forge!4518
2021-04-15 15:27:08 +00:00
Anthony Calosa
816bb2008a update zip decompression
closes #1802
2021-04-15 23:18:40 +08:00
Anthony Calosa
8eb430dffd Merge branch 'kevlahnota-master-patch-49827' into 'master'
Update CardArchetypeLDAGenerator.java

See merge request core-developers/forge!4517
2021-04-15 10:08:11 +00:00
Anthony Calosa
641b5af8eb Update CardArchetypeLDAGenerator.java 2021-04-15 10:06:33 +00:00
Anthony Calosa
38ef1e4ed9 Merge branch 'revert-ed5f5d3c' into 'master'
Revert "Update LDAModelGenetrator.java"

See merge request core-developers/forge!4516
2021-04-15 09:48:01 +00:00
Anthony Calosa
123c41b4e3 Revert "Update LDAModelGenetrator.java"
This reverts commit ed5f5d3c6b
2021-04-15 09:47:31 +00:00
Anthony Calosa
7001f17c88 Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!4514
2021-04-15 09:42:53 +00:00
Anthony Calosa
1f10191bc7 Merge branch 'kevlahnota-master-patch-10151' into 'master'
Update LDAModelGenetrator.java

See merge request core-developers/forge!4515
2021-04-15 09:42:23 +00:00
Anthony Calosa
ed5f5d3c6b Update LDAModelGenetrator.java 2021-04-15 09:42:04 +00:00
CCTV-1
a03bb7a353 translate new string. 2021-04-15 16:49:58 +08:00
Lyu Zong-Hong
44d4eae8a1 Filter more cards in getTargetableCards 2021-04-15 16:15:59 +09:00
Michael Kamensky
22ffcf5660 Merge branch 'historicLDA' into 'master'
Historic Archetype Random Deck Generation

See merge request core-developers/forge!4488
2021-04-15 06:30:19 +00:00
Michael Kamensky
f40ea6cc8d Merge branch 'rainbow' into 'master'
Fix DelayedTriggers running when card owner left game

See merge request core-developers/forge!4512
2021-04-15 06:30:05 +00:00
Hans Mackowiak
adfcb2b4bf Merge branch 'facedownYedoraMutate' into 'master'
Card: changed some logic with facedown and copy states

See merge request core-developers/forge!4506
2021-04-15 05:59:29 +00:00
Hans Mackowiak
8d42be3bac Card: changed some logic with facedown and copy states 2021-04-15 05:59:28 +00:00
tool4EvEr
e2036a5203 Clean delayedTriggers from player who lost 2021-04-14 22:58:08 +02:00
tool4EvEr
7107f05adb Fix DelayedTriggers running when card owner left game 2021-04-14 22:12:14 +02:00
austinio7116
529fc42b72 Merge branch 'coremaster' into historicLDA
# Conflicts:
#	forge-gui/res/formats/Sanctioned/Historic.txt
2021-04-14 19:09:01 +01:00
Michael Kamensky
7808605a8c Merge branch 'fix' into 'master'
Some fixes

See merge request core-developers/forge!4508
2021-04-14 18:08:07 +00:00
Michael Kamensky
0930013cc2 Merge branch 'card-fixes' into 'master'
Adrix and Nev only applies to you.

See merge request core-developers/forge!4509
2021-04-14 18:07:58 +00:00
Michael Kamensky
51054b70d6 Merge branch 'codie' into 'master'
[C21] Codie tweaks

See merge request core-developers/forge!4507
2021-04-14 18:07:20 +00:00
Michael Kamensky
471fb621e5 Merge branch 'flagbearer' into 'master'
Add Flagbearer

See merge request core-developers/forge!4495
2021-04-14 18:06:56 +00:00
Alumi
1e0c3e7621 Check if chosen targets meets MustTarget requirements in setupTargets() too. (For spell that has multiple targeting SAs) 2021-04-14 18:06:55 +00:00
Northmoc
a75d312770 oversimplify.txt - RememberLKI needed 2021-04-14 13:21:52 -04:00
austinio7116
0fd2ec58be Merge remote-tracking branch 'Core/master' into coremaster 2021-04-14 18:13:19 +01:00
Northmoc
be484a8077 fix oversimplify.txt 2021-04-14 13:07:06 -04:00
Adam Pantel
3d8388ad92 Adrix and Nev only applies to you. 2021-04-14 12:52:00 -04:00
Northmoc
7a5eac3621 support "an opponent" choosing for multiplayer (and add AI hint) 2021-04-14 12:19:31 -04:00
Hans Mackowiak
ce34e937d6 CostExile: fixed exile cost with mana value X 2021-04-14 17:48:42 +02:00
Northmoc
c8646208f0 codie_vociferous_codex.txt add AI hints 2021-04-14 11:39:20 -04:00
Northmoc
3ed83e4f59 codie_vociferous_codex.txt - add ThisTurn$ to delayed trigger - add MayPlay Effect 2021-04-14 11:33:20 -04:00
Northmoc
ebe6a12555 allow no reordering on zones other than library 2021-04-14 11:28:59 -04:00
Northmoc
1727456487 digsite_engineer.txt DB -> AB 2021-04-14 10:37:29 -04:00
swordshine
ef895777ca Merge branch 'master' into 'master'
Card cleanup and Historic update

See merge request core-developers/forge!4505
2021-04-14 10:58:48 +00:00
swordshine
33fa10e9c1 Merge branch 'emblazoned_golem_remove_max_limit' into 'master'
Remove X max limit for Emblazoned Golem

See merge request core-developers/forge!4503
2021-04-14 10:56:51 +00:00
Hythonia
1f9094caec Removed semicolon 2021-04-14 12:43:33 +02:00
Hythonia
9a5c58aae8 Add Strixhaven and Mystical Archives 2021-04-14 12:41:40 +02:00
Hythonia
1b1e6c394d Card cleanup and Historic update 2021-04-14 12:38:17 +02:00
Anthony Calosa
504872ac1e Merge branch 'master' into 'master'
remove extra svar

See merge request core-developers/forge!4504
2021-04-14 10:07:40 +00:00
Anthony Calosa
edc40f4a6c remove extra svar 2021-04-14 18:06:27 +08:00
Lyu Zong-Hong
7b6e08f791 Remove X max limit for Emblazoned Golem 2021-04-14 16:29:57 +09:00
Michael Kamensky
b184db2ee6 Merge branch 'morophon' into 'master'
Add AILogic to Morophon, the Boundless and move two scripts into correct folders

See merge request core-developers/forge!4500
2021-04-14 06:37:04 +00:00
Michael Kamensky
d10bf7fa35 Merge branch 'fixplay' into 'master'
Once Upon A Time: Fix AltCost

Closes #1801

See merge request core-developers/forge!4501
2021-04-14 06:36:54 +00:00
Michael Kamensky
ce20d21f4c Merge branch 'khm_rankings' into 'master'
KHM updated rankings + nonbasic land rankings!

See merge request core-developers/forge!4502
2021-04-14 06:36:47 +00:00
Michael Kamensky
48a35746a0 Merge branch 'c21_11' into 'master'
promise_of_loyalty.txt

See merge request core-developers/forge!4485
2021-04-14 06:33:19 +00:00
swordshine
bf9afd4ba1 Merge branch 'card-fixes' into 'master'
Card fixes

See merge request core-developers/forge!4499
2021-04-14 04:07:13 +00:00
Northmoc
0eb2d59ecc add AI DeckHas 2021-04-13 20:07:13 -04:00
Northmoc
b9bcfef817 promise_of_loyalty.txt 2021-04-13 20:07:13 -04:00
Northmoc
27d01500fa KHM updated rankings + nonbasic land rankings! 2021-04-13 16:39:47 -04:00
paul_snoops
d2ea3e4986 Add AILogic to Morophon, the Boundless and move to scripts into correct folders 2021-04-13 18:07:52 +01:00
Adam Pantel
b0cecebe34 Card fixes 2021-04-13 12:48:30 -04:00
tool4EvEr
f4971589c6 Fix AltCost 2021-04-13 18:26:34 +02:00
Bug Hunter
506d64dc8b Merge branch 'pennant' into 'master'
Team Pennant: Fix typo

See merge request core-developers/forge!4498
2021-04-13 16:18:36 +00:00
tool4EvEr
f56aa6cc29 Fix typo 2021-04-13 18:17:51 +02:00
Bug Hunter
a17ec8c2db Merge branch 'admiral' into 'master'
Azure Fleet Admiral: Fix typo

See merge request core-developers/forge!4497
2021-04-13 15:56:25 +00:00
tool4EvEr
a718986e2d Fix typo 2021-04-13 17:55:57 +02:00
Michael Kamensky
242e0e7fdc Merge branch 'stx_draft_fix' into 'master'
fix:stx booster missing sta and lesson cards.

See merge request core-developers/forge!4493
2021-04-13 13:40:28 +00:00
Anthony Calosa
75f7cb6df7 Merge branch 'master' into 'master'
update cards

See merge request core-developers/forge!4496
2021-04-13 13:16:01 +00:00
Anthony Calosa
cf7d0792a2 update cards 2021-04-13 21:13:46 +08:00
Hans Mackowiak
29d260db9d Cards: use CharacteristicDefining for Alternative Cost for now 2021-04-13 10:32:16 +02:00
austinio7116
d7dfad1fe8 More Historic LDA data 2021-04-13 08:27:41 +01:00
CCTV-1
82f404e627 each draft boosters should be contain 1 sta card and 1 lesson card. 2021-04-13 14:08:56 +08:00
Anthony Calosa
c49b4c45c8 Merge branch 'kevlahnota-master-patch-11851' into 'master'
finalize Commander 2021.txt

See merge request core-developers/forge!4492
2021-04-13 05:11:16 +00:00
Anthony Calosa
39f3feb210 Update Commander 2021.txt 2021-04-13 05:10:27 +00:00
Michael Kamensky
4ed89e9515 Merge branch 'bangchuckersAI' into 'master'
FlipACoinAI: add logic for Goblin Bangchuckers

Closes #1666

See merge request core-developers/forge!4487
2021-04-13 04:40:07 +00:00
Michael Kamensky
0b6989c60c Merge branch 'update_japanese_localization' into 'master'
Update Japanese Localization

See merge request core-developers/forge!4491
2021-04-13 04:39:12 +00:00
Michael Kamensky
a9139c6023 Merge branch 'nametypos' into 'master'
Make Your Mark fixes and some typos

See merge request core-developers/forge!4486
2021-04-13 04:39:00 +00:00
Michael Kamensky
2de580236e Merge branch 'refine' into 'master'
Gifts Ungiven and Intuition: eliminate ChooseCard line

See merge request core-developers/forge!4489
2021-04-13 04:38:32 +00:00
Lyu Zong-Hong
e5d4979f3e Update Japanese Localization 2021-04-13 10:13:51 +09:00
Rob Schnautz
8f00de273a october 12, 2020 banned and restricted announcement 2021-04-13 00:15:55 +00:00
Rob Schnautz
1a0f5a7460 september 28, 2020 banned and restricted announcement 2021-04-13 00:11:26 +00:00
Rob Schnautz
b0229fb528 zendikar rising historic 2021-04-13 00:07:25 +00:00
Rob Schnautz
454f1aa00a jumpstart historic 2021-04-12 23:50:25 +00:00
Rob Schnautz
12c25d786f core set 2021 historic 2021-04-12 23:46:35 +00:00
Rob Schnautz
7c65eca1fb ikoria commander historic 2021-04-12 23:40:10 +00:00
Rob Schnautz
6a87988d4c commander 2019 historic 2021-04-12 23:38:32 +00:00
Rob Schnautz
b65a17c867 commander 2018 historic 2021-04-12 23:36:11 +00:00
Rob Schnautz
f067fc9896 commander 2017 historic 2021-04-12 23:33:12 +00:00
Rob Schnautz
36cc53b4f8 commander 2016 historic 2021-04-12 23:29:37 +00:00
Rob Schnautz
497df9db96 commander 2015 historic formats 2021-04-12 23:25:53 +00:00
Rob Schnautz
26f66b0377 commander 2014 in historic formats 2021-04-12 23:20:49 +00:00
Rob Schnautz
69c448f4e6 commander 2013 in legacy / vintage historic formats 2021-04-12 23:12:56 +00:00
Rob Schnautz
df08191857 add commander to legacy and vintage historic formats 2021-04-12 23:06:52 +00:00
Rob Schnautz
bb88ff7649 ikoria lair of behemoths historic formats 2021-04-12 22:46:39 +00:00
Rob Schnautz
7329bf8eef theros beyond death historic formats 2021-04-12 22:42:40 +00:00
Rob Schnautz
a58e409665 january 13, 2020 banned and restricted announcement 2021-04-12 22:33:45 +00:00
Rob Schnautz
511a55e22c november 18, 2019 banned and restricted announcement 2021-04-12 22:30:45 +00:00
Rob Schnautz
cfa2cfeded november 4, 2019 pioneer banned and restricted announcement 2021-04-12 22:17:57 +00:00
Rob Schnautz
9d30414539 pioneer 2021-04-12 22:15:55 +00:00
Rob Schnautz
1b34de81eb october 21, 2019 banned and restricted announcement 2021-04-12 22:08:05 +00:00
Rob Schnautz
827824500b throne of eldraine historic formats 2021-04-12 22:01:23 +00:00
Rob Schnautz
35198ddbe8 august 26, 2019 banned and restricted announcement 2021-04-12 21:54:00 +00:00
Rob Schnautz
546545a800 M20 historic formats 2021-04-12 21:42:06 +00:00
Northmoc
1023cc5cb2 make_your_mark.txt remove unneeded cleanup 2021-04-12 17:15:56 -04:00
Northmoc
094cf9e400 make_your_mark.txt improve Effect behavior + add cleanup 2021-04-12 13:30:33 -04:00
Northmoc
1bc834efe8 add DeckHints 2021-04-12 13:24:32 -04:00
Northmoc
b4b039b2b0 intuition.txt eliminate ChooseCard line 2021-04-12 13:03:54 -04:00
Hans Mackowiak
d8e850673b StaticAbilityContinuous: fix GainsAbilitiesOfDefined 2021-04-12 17:56:42 +02:00
Northmoc
35d7046d69 gifts_ungiven.txt eliminate ChooseCard line 2021-04-12 11:20:36 -04:00
austinio7116
fad87abfea Historic Random Deck Generation initial data 2021-04-12 15:24:45 +01:00
Hans Mackowiak
10d10ff18d FlipACoinAI: add logic for Goblin Bangchuckers 2021-04-12 16:17:47 +02:00
Northmoc
161b99f252 elven_bow.txt remove default/bad params and desc typo 2021-04-12 09:53:58 -04:00
Northmoc
87ff260c9f dwarven_hammer.txt remove default/bad params 2021-04-12 09:51:18 -04:00
Northmoc
cc4dafa400 draugrs_helm.txt remove default/bad params 2021-04-12 09:50:22 -04:00
Northmoc
9d29904ec5 valkyries_sword.txt remove default/bad params 2021-04-12 09:48:55 -04:00
austinio7116
50be4f4ef8 Merge branch 'coremaster' into historicLDA 2021-04-12 14:46:43 +01:00
austinio7116
5f9c05080f Merge remote-tracking branch 'Core/master' into coremaster 2021-04-12 14:46:33 +01:00
Northmoc
bbfcc87a68 giants_amulet.txt tidy up 2021-04-12 09:46:00 -04:00
austinio7116
d7e9510353 Merge branch 'keeptrying' into 'master'
Improved deck naming removing "-" and updated pioneer deckgen data

See merge request core-developers/forge!4484
2021-04-12 13:45:09 +00:00
austinio7116
1a1057f56f Improved deck naming removing "-" and updated pioneer deckgen data 2021-04-12 13:45:09 +00:00
austinio7116
93ff8cfe1b Added Historic Random Deck Generation 2021-04-12 14:36:57 +01:00
Northmoc
4d05e085ef raiders_karve.txt fix TrigDesc 2021-04-12 09:24:25 -04:00
Northmoc
d2d96d2ece make_your_mark.txt 2021-04-12 09:23:59 -04:00
austinio7116
78762cd479 Reverted accidental test change 2021-04-12 13:40:44 +01:00
austinio7116
cdaa37c5fd Initial basic fix for LDA module to ensure it builds without errors - also removed some unneeded imports which the Maven checks were blocking on 2021-04-12 13:39:49 +01:00
austinio7116
76140fb555 Reverted accidental test change 2021-04-12 13:37:54 +01:00
austinio7116
352c5ea3d3 Corrected version in pom 2021-04-12 13:36:01 +01:00
austinio7116
f167d4fa3b Merge branch 'ldabuildonly2' of https://git.cardforge.org/Austin/forge into ldabuildonly2 2021-04-12 13:32:36 +01:00
austinio7116
7528cd3b58 Merge branch 'master' into 'ldabuildonly2'
# Conflicts:
#   forge-gui/src/main/java/forge/gamemodes/limited/ArchetypeDeckBuilder.java
2021-04-12 12:31:42 +00:00
austinio7116
cab8d0fb97 Merge branch 'coremaster' into ldabuildonly2
# Conflicts:
#	forge-gui/src/main/java/forge/gamemodes/limited/ArchetypeDeckBuilder.java
2021-04-12 13:11:52 +01:00
austinio7116
c7d9c174fd Merge remote-tracking branch 'Core/master' into coremaster 2021-04-12 13:02:52 +01:00
Michael Kamensky
b7787c8cf6 Merge branch 'Williams-master-patch-65541' into 'master'
C21 Revival Experiment

See merge request core-developers/forge!4480
2021-04-12 11:29:27 +00:00
Anthony Calosa
4d218e3624 Merge branch 'master' into 'master'
unused import

See merge request core-developers/forge!4482
2021-04-12 10:22:46 +00:00
Anthony Calosa
89fab45eb7 unused import 2021-04-12 18:21:54 +08:00
Anthony Calosa
9bca338cc2 Merge branch 'kevlahnota-master-patch-58825' into 'master'
Update Commander 2021.txt

See merge request core-developers/forge!4481
2021-04-12 10:16:33 +00:00
Anthony Calosa
36c3c43f86 Merge branch 'master' into 'master'
Simplify ManaConversion scripts, remove Flash/Ice Cave technical debt

See merge request core-developers/forge!4479
2021-04-12 10:14:23 +00:00
Anthony Calosa
d9f762c485 Update Commander 2021.txt 2021-04-12 10:04:33 +00:00
John
3d206d1fb1 Update revival_experiment.txt 2021-04-12 09:46:24 +00:00
John
2a959f23d9 Add new file 2021-04-12 09:44:53 +00:00
Hythonia
bb659d39db ManaConvert keyword display 2021-04-12 10:03:03 +02:00
Hythonia
49cdd4d36d DefinedCost and DefinedManaCost 2021-04-12 10:03:02 +02:00
Hythonia
41d55cf780 Simplify ManaConvertion scripts 2021-04-12 10:03:02 +02:00
Michael Kamensky
5c5c948193 Merge branch 'card-fixes' into 'master'
Moritte type fix

See merge request core-developers/forge!4477
2021-04-12 04:19:37 +00:00
Michael Kamensky
0e32dc982e Merge branch 'unset-cards' into 'master'
Just host cards

See merge request core-developers/forge!4460
2021-04-12 04:19:30 +00:00
Michael Kamensky
9fffdb8cad Merge branch 'mannequin' into 'master'
Makeshift Mannequin: Fix duration

See merge request core-developers/forge!4474
2021-04-12 04:17:47 +00:00
Michael Kamensky
76a1da2d0f Merge branch 'emblazoned_golem' into 'master'
Add Emblazoned Golem

See merge request core-developers/forge!4467
2021-04-12 04:17:33 +00:00
Adam Pantel
ba012a4b0e Moritte type fix 2021-04-11 21:54:28 -04:00
Adam Pantel
09fd212e29 Remove // from names 2021-04-11 21:53:01 -04:00
swordshine
54fd4787fd Merge branch 'kaboom' into 'master'
Kaboom!

See merge request core-developers/forge!4472
2021-04-12 01:04:57 +00:00
Hans Mackowiak
023499a6c0 Player: run checkStaticAbilities before Companion to apply CDA 2021-04-12 02:03:59 +02:00
Lyu Zong-Hong
a8ad7f540b Add DeckHas hint 2021-04-12 08:01:20 +09:00
Adam Pantel
b7ee2c696b Some host cards 2021-04-11 18:35:03 -04:00
austinio7116
4528223dee Initial basic fix for LDA module to ensure it builds without errors - also removed some unneeded imports which the Maven checks were blocking on 2021-04-11 23:13:49 +01:00
austinio7116
cf729db0c0 Merge remote-tracking branch 'Core/master' into coremaster 2021-04-11 23:13:14 +01:00
austinio7116
a4deafa238 Merge branch 'khmldabranch' into 'master'
KHM deck generation

See merge request core-developers/forge!4469
2021-04-11 22:12:02 +00:00
austinio7116
5e88eab617 Modern LDA data 2021-04-11 22:46:47 +01:00
austinio7116
950e51e0fe Improved snow land decision making on LDA deck generator 2021-04-11 22:46:43 +01:00
austinio7116
1c1d743638 Fixes for snow covered lands in LDA deck generation 2021-04-11 22:46:38 +01:00
austinio7116
ffa7e39121 Merge remote-tracking branch 'Core/master' into coremaster 2021-04-11 22:46:01 +01:00
austinio7116
84d6a24446 Modern LDA data 2021-04-11 22:45:09 +01:00
tool4EvEr
91c92d4b64 Fix duration 2021-04-11 21:27:50 +02:00
Bug Hunter
056229e8ee Merge branch 'impetus' into 'master'
Psychic Impetus: Fix missing param

See merge request core-developers/forge!4473
2021-04-11 19:22:12 +00:00
tool4EvEr
c4c5f7ab6b Fix missing param 2021-04-11 21:21:39 +02:00
tool4EvEr
c7ab33f3ab Oracle adjustment 2021-04-11 21:12:48 +02:00
Michael Kamensky
3a3850ec4e Merge branch 'Williams-master-patch-09539' into 'master'
C21 Sproutback Trudge

See merge request core-developers/forge!4455
2021-04-11 19:04:23 +00:00
Michael Kamensky
84ed014909 Merge branch 'new-cards-2' into 'master'
Combat Calligrapher

Closes #1795

See merge request core-developers/forge!4462
2021-04-11 19:03:57 +00:00
Michael Kamensky
39563682f5 Merge branch 'phasedAttachment' into 'master'
GameEntity: when attachment is phased out, treat as not attached

See merge request core-developers/forge!4470
2021-04-11 19:03:26 +00:00
Michael Kamensky
53179f6e3f Merge branch 'triggerCounterPlayerAddedAll' into 'master'
TriggerCounterPlayerAddedAll: new Trigger

See merge request core-developers/forge!4454
2021-04-11 19:03:12 +00:00
Michael Kamensky
b1d5ba6787 Merge branch 'zaffai' into 'master'
Zaffai: Fix trigger

See merge request core-developers/forge!4471
2021-04-11 19:02:56 +00:00
John
361a3d0371 Add new file 2021-04-11 19:01:59 +00:00
tool4EvEr
6b5b38535e Zaffai: Fix trigger 2021-04-11 18:14:31 +02:00
austinio7116
8a4539b19a Improved snow land decision making on LDA deck generator 2021-04-11 16:06:53 +01:00
Hans Mackowiak
f6ee232d9e GameEntity: when attachment is phased out, treat as not attached 2021-04-11 17:00:20 +02:00
austinio7116
2562d28f4f Fixes for snow covered lands in LDA deck generation 2021-04-11 15:50:02 +01:00
austinio7116
f89cfa3ec8 KHM deck generation 2021-04-11 15:23:22 +01:00
Hans Mackowiak
d8b665f64f TriggerCounterPlayerAddedAll: new Trigger that triggers when a player puts more counters on one object 2021-04-11 15:53:32 +02:00
Adam Pantel
d473737ca8 Remove triggers in setup game state 2021-04-11 09:28:16 -04:00
John
52e7cc41e2 Update sproutback_trudge.txt 2021-04-11 13:08:32 +00:00
austinio7116
53978333fb KHM deck generation 2021-04-11 13:59:58 +01:00
Michael Kamensky
183209baa3 Merge branch 'arlinn' into 'master'
Fix Arlinn Kord transform

See merge request core-developers/forge!4458
2021-04-11 12:43:34 +00:00
Michael Kamensky
0df4e1147c Merge branch 'yedora' into 'master'
Yedora: allow morph

See merge request core-developers/forge!4466
2021-04-11 12:43:26 +00:00
Michael Kamensky
7c42b1f845 Merge branch 'stxrank' into 'master'
STX draftsim ranks

See merge request core-developers/forge!4463
2021-04-11 12:41:21 +00:00
Michael Kamensky
129e4762dc Merge branch 'new-cards' into 'master'
Nils, Discipline Enforcer (attacking tax computation)

See merge request core-developers/forge!4457
2021-04-11 12:40:38 +00:00
Michael Kamensky
fc1c5ca6e9 Merge branch 'fixlang' into 'master'
CardTranslation: Add fallback for english only cards

See merge request core-developers/forge!4456
2021-04-11 12:40:19 +00:00
Lyu Zong-Hong
8ae916ac28 Add Emblazoned Golem 2021-04-11 18:56:49 +09:00
tool4EvEr
0ccf9c961b Yedora: allow morph 2021-04-11 10:27:34 +02:00
Bug Hunter
b894e65618 Merge branch 'TRT-master-patch-28302' into 'master'
Update forge-gui/res/cardsfolder/upcoming/dream_strix.txt

See merge request core-developers/forge!4465
2021-04-11 08:25:14 +00:00
Bug Hunter
801d31ed29 Update forge-gui/res/cardsfolder/upcoming/dream_strix.txt 2021-04-11 08:25:03 +00:00
Anthony Calosa
34fb9bb865 Merge branch 'kevlahnota-master-patch-52335' into 'master'
Update Commander 2021.txt

See merge request core-developers/forge!4464
2021-04-11 07:18:17 +00:00
Anthony Calosa
b5201e17ff Update forge-gui/res/editions/Commander 2021.txt 2021-04-11 07:17:16 +00:00
Anthony Calosa
204bbfc6c7 Update forge-gui/res/editions/Commander 2021.txt 2021-04-11 07:12:00 +00:00
Anthony Calosa
692fe0c885 Update Commander 2021.txt 2021-04-11 07:00:32 +00:00
John
3a372179d6 Update sproutback_trudge.txt 2021-04-11 06:51:44 +00:00
Adam Pantel
3de8bd49ad Combat Calligrapher 2021-04-11 01:59:36 -04:00
austinio7116
b9c02d5d99 Merge branch 'ldastream' into 'master'
LDA module

See merge request core-developers/forge!4251
2021-04-11 05:51:11 +00:00
Hans Mackowiak
185c4c09c9 Changed card-based deck generation to archetype based making better use of the new LDA models. Decks can now be selected by archetype with names generated from the source decklists. Archetypes are ordered by popularity. 2021-04-11 05:51:11 +00:00
austinio7116
ad709fd8f0 STX draftsim ranks 2021-04-11 06:47:57 +01:00
Adam Pantel
da7a1f34e1 Nils, Discipline Enforcer 2021-04-11 00:40:27 -04:00
Michael Kamensky
47d2f3b6ad Merge branch 'new-cards-2' into 'master'
C21: Stinging Study

See merge request core-developers/forge!4461
2021-04-11 04:22:59 +00:00
Michael Kamensky
673759f17b Merge branch 'city' into 'master'
Fix originally printed property

Closes #1797

See merge request core-developers/forge!4453
2021-04-11 04:20:44 +00:00
Adam Pantel
0e2d6ac196 C21: Stinging Study 2021-04-11 00:16:00 -04:00
Anthony Calosa
eb99915912 Merge branch 'master' into 'master'
update ImageKeys

See merge request core-developers/forge!4459
2021-04-11 02:22:04 +00:00
Anthony Calosa
501df02260 update ImageKeys 2021-04-11 10:03:56 +08:00
tool4EvEr
d74f6dba0a Fix Arlinn Kord transform 2021-04-10 23:28:11 +02:00
tool4EvEr
b590c6b55a CardTranslation: Add fallback for english only cards 2021-04-10 20:00:48 +02:00
John
175f38f114 Add new file 2021-04-10 16:53:05 +00:00
Michael Kamensky
44f69b2b0b Merge branch 'fixX' into 'master'
Fix Thieving Skydiver 0 kicker

See merge request core-developers/forge!4450
2021-04-10 16:28:25 +00:00
Michael Kamensky
02a1b139bf Merge branch 'lib' into 'master'
Sylvan Library: Add ordering choice

See merge request core-developers/forge!4452
2021-04-10 16:27:52 +00:00
Michael Kamensky
326cf81a0f Merge branch 'Williams-master-patch-30398' into 'master'
C21 Suthro 10/04

See merge request core-developers/forge!4448
2021-04-10 16:27:45 +00:00
John
8952ddc9dc Update witchs_clinic.txt 2021-04-10 15:31:37 +00:00
John
dfe2c6d064 Update veinwitch_coven.txt 2021-04-10 15:29:31 +00:00
John
fea2f30a1c Update veinwitch_coven.txt 2021-04-10 15:28:55 +00:00
tool4EvEr
5d8bd256f1 Fix originally printed property 2021-04-10 17:28:18 +02:00
John
d7f41f4a56 Add new file 2021-04-10 15:28:18 +00:00
John
9169ea6ebf Update trudge_garden.txt 2021-04-10 15:24:54 +00:00
John
20314c8952 Update trudge_garden.txt 2021-04-10 15:24:00 +00:00
John
f262075d2a Update pest_infestation.txt 2021-04-10 15:21:52 +00:00
John
37d9ea103b Update gyome_master_chef.txt 2021-04-10 15:21:20 +00:00
John
ef51f23308 Update ezzaroot_channeler.txt 2021-04-10 15:20:46 +00:00
John
11f75176b8 Update blossoming_bogbeast.txt 2021-04-10 15:15:33 +00:00
John
bd676c7c5c Update blight_mound.txt 2021-04-10 15:05:17 +00:00
tool4EvEr
ccf51356a9 Sylvan Library: Add ordering choice 2021-04-10 16:13:07 +02:00
tool4EvEr
ece32cbe70 Join XCantBe0 restrictions from both parts when combining costs 2021-04-10 13:51:16 +02:00
Bug Hunter
574e74350c Merge branch 'fixdispute' into 'master'
Academic Dispute: Fix optional

See merge request core-developers/forge!4451
2021-04-10 10:41:27 +00:00
tool4EvEr
4665f8c6c6 Academic Dispute: Fix optional 2021-04-10 12:40:32 +02:00
tool4EvEr
a1bdd1fc47 Fix Thieving Skydiver 0 kicker 2021-04-10 12:35:47 +02:00
Bug Hunter
cdcb801b50 Merge branch 'TRT-master-patch-77164' into 'master'
Update forge-gui/res/cardsfolder/upcoming/explosive_welcome.txt

See merge request core-developers/forge!4449
2021-04-10 08:39:38 +00:00
Bug Hunter
5f4bfed2b5 Update forge-gui/res/cardsfolder/upcoming/explosive_welcome.txt 2021-04-10 08:39:23 +00:00
John
cffaa6fef8 Add new file 2021-04-10 07:09:15 +00:00
John
bf2e5392f8 Add new file 2021-04-10 07:08:31 +00:00
John
de6c93b849 Add new file 2021-04-10 07:07:47 +00:00
John
3b6bbe4799 Add new file 2021-04-10 07:06:05 +00:00
John
62250cd41f Add new file 2021-04-10 07:05:08 +00:00
John
69a3963de0 Add new file 2021-04-10 07:04:23 +00:00
John
acefebfcd7 Add new file 2021-04-10 07:03:27 +00:00
John
b8f7d084c7 Add new file 2021-04-10 07:02:42 +00:00
John
fd420b3d81 Add new file 2021-04-10 07:01:26 +00:00
John
bf799869ef Add new file 2021-04-10 07:00:43 +00:00
John
8fc3004349 Add new file 2021-04-10 06:58:06 +00:00
Michael Kamensky
13b22e2047 Merge branch 'c21_9a' into 'master'
C21 - 9 April (more)

See merge request core-developers/forge!4446
2021-04-10 04:14:10 +00:00
Michael Kamensky
2a33ba9f93 Merge branch 'fixstuff' into 'master'
Fix some stuff

See merge request core-developers/forge!4442
2021-04-10 04:13:47 +00:00
Michael Kamensky
ad09233d4c Merge branch 'clash' into 'master'
ClashEffect: Fix triggering for both players

See merge request core-developers/forge!4436
2021-04-10 04:13:29 +00:00
Bug Hunter
2be3a38bb0 ClashEffect: Fix triggering for both players 2021-04-10 04:13:29 +00:00
Michael Kamensky
26467221b3 Merge branch '1759-extra-generated-text-on-auras' into 'master'
Resolve "Extra generated text on Auras"

Closes #1759

See merge request core-developers/forge!4443
2021-04-10 04:13:12 +00:00
Michael Kamensky
dd19ad356a Merge branch 'guardian-archon' into 'master'
[C21] Guardian Archon

See merge request core-developers/forge!4445
2021-04-10 04:12:48 +00:00
Michael Kamensky
4fe91e9a6a Merge branch 'fix_psychic_battle' into 'master'
Fix Psychic Battle in multiplayer game

See merge request core-developers/forge!4447
2021-04-10 04:12:13 +00:00
Michael Kamensky
63d660d152 Merge branch 'noAbilities' into 'master'
Fix NoAbilities failing for basic land types

See merge request core-developers/forge!4424
2021-04-10 04:12:07 +00:00
Lyu Zong-Hong
730ff1758f Fix Psychic Battle in multiplayer game 2021-04-10 10:10:21 +09:00
Northmoc
174099dd1d tempting_contract.txt 2021-04-09 19:58:18 -04:00
Northmoc
f7f49b98ff healing_technique.txt (Suthro) 2021-04-09 19:05:23 -04:00
Northmoc
18f0af1803 guardian_archon.txt 2021-04-09 17:54:04 -04:00
Northmoc
af2a4e1ce4 tidy up ChosenPlayer for DefinedKW 2021-04-09 17:53:49 -04:00
Bug Hunter
a4a58d9a06 Merge branch 'TRT-master-patch-75766' into 'master'
Update forge-gui/res/cardsfolder/upcoming/uvilda_dean_of_perfection_nassari_dean_of_expression.txt

See merge request core-developers/forge!4444
2021-04-09 20:08:23 +00:00
Bug Hunter
861bb5f608 Update forge-gui/res/cardsfolder/upcoming/uvilda_dean_of_perfection_nassari_dean_of_expression.txt 2021-04-09 20:07:32 +00:00
Hans Mackowiak
36c7f1244d Card: fix faulty SpellDescriptions, skip BasicSpells 2021-04-09 20:57:36 +02:00
Michael Kamensky
8e4d07163d Merge branch 'twosat-master-patch-58716' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4440
2021-04-09 18:21:29 +00:00
Michael Kamensky
1af3cd40c0 Merge branch 'c21_9' into 'master'
C21 - 9 April

See merge request core-developers/forge!4441
2021-04-09 18:21:09 +00:00
Michael Kamensky
8e0eb9d110 Merge branch 'c21_8' into 'master'
C21 - 8 April

See merge request core-developers/forge!4434
2021-04-09 18:20:40 +00:00
Northmoc
bf7dbe0098 fain_the_broker.txt 2021-04-09 13:38:58 -04:00
Northmoc
f37d27f803 blot_out_the_sky.txt fix TokenScript 2021-04-09 13:34:25 -04:00
Northmoc
b98d2fbaa4 quicksilver_dagger.txt clean up Descs 2021-04-09 13:34:03 -04:00
Northmoc
181539a424 keen_duelist.txt 2021-04-09 13:01:46 -04:00
Hans Mackowiak
6fe299d7c8 StaticAbilityPanharmonicon: use LKI only for ChangeZone Trigger 2021-04-09 18:56:52 +02:00
Andreas Bendel
585eb23b3c Update de-DE.properties
translated lblLearnALesson
2021-04-09 15:35:14 +00:00
Hans Mackowiak
04940fc4db StaticAbilityPanharmonicon: clean up use matchesValidParam 2021-04-09 17:28:33 +02:00
Hans Mackowiak
75fa1b7fbe Merge branch 'master' into 'master'
C21 Veyran & Panharmonicon rework

See merge request core-developers/forge!4408
2021-04-09 15:15:30 +00:00
Hythonia
cc8e8b2dd1 C21 Veyran & Panharmonicon rework 2021-04-09 15:15:29 +00:00
Northmoc
0b3bbad98f incarnation_technique.txt 2021-04-09 10:13:44 -04:00
Northmoc
9da11e05ee strixhaven_stadium.txt add TriggerZones$ 2021-04-09 09:57:36 -04:00
Northmoc
ecca6f8eda cunning_rhetoric.txt 2021-04-09 09:40:47 -04:00
Northmoc
b5c6f32d1f author_of_shadows.txt 2021-04-09 09:40:46 -04:00
Northmoc
f38c083b05 inkshield.txt 2021-04-09 09:40:46 -04:00
Northmoc
598a9e0918 felisa_fang_of_silverquill.txt (Suthro) 2021-04-09 09:40:45 -04:00
Michael Kamensky
c93c582ed5 Merge branch 'master' into 'master'
Net Deck Archive Updates

See merge request core-developers/forge!4437
2021-04-09 07:13:21 +00:00
Michael Kamensky
010cfb6b35 Merge branch 'lands' into 'master'
Improve AI land handling

See merge request core-developers/forge!4435
2021-04-09 07:13:19 +00:00
Michael Kamensky
b1d95a5fa0 Merge branch 'fixes' into 'master'
Fixes

See merge request core-developers/forge!4433
2021-04-09 07:10:27 +00:00
Michael Kamensky
0d4bf2ea2e Merge branch 'sponsor' into 'master'
Scholarship Sponsor [C21]

See merge request core-developers/forge!4438
2021-04-09 07:10:16 +00:00
Michael Kamensky
d4eb451ba6 Merge branch 'psychic_battle' into 'master'
Add Psychic Battle

See merge request core-developers/forge!4439
2021-04-09 07:09:52 +00:00
Lyu Zong-Hong
9435701d10 Fix bug that when AI cast divide damage as you choose spells, human can't change targetcorrectly 2021-04-09 15:12:13 +09:00
Lyu Zong-Hong
7603943c38 Add Psychic Battle 2021-04-09 14:06:51 +09:00
Northmoc
a959f79bcf scholarship_sponsor.txt 2021-04-08 22:33:26 -04:00
Northmoc
9899d72dce witherbloom_command.txt fix Mill NumCards 2021-04-08 21:27:58 -04:00
Churrufli
4263ea6881 Net Deck Archive Updates 2021-04-09 00:07:21 +02:00
Adam Pantel
ab287858b3 Improve land handling 2021-04-08 16:05:54 -04:00
tool4EvEr
eb6d168d50 Update NoAbilities logic 2021-04-08 20:40:23 +02:00
Michael Kamensky
52b8c4ae5e Merge branch 'master' into 'master'
translate learnLesson message.

See merge request core-developers/forge!4432
2021-04-08 17:53:37 +00:00
tool4EvEr
1c4640d69b Fix NoAbilities failing for basic land types 2021-04-08 19:42:18 +02:00
Northmoc
3827459229 - pic line 2021-04-08 13:20:21 -04:00
Northmoc
aaeb9f9e84 ajanis_aid.txt Rep Eff: CombatDamage -> IsCombat 2021-04-08 13:19:25 -04:00
Northmoc
61acb3e42b jeska_thrice_reborn.txt Rep Eff: CombatDamage -> IsCombat 2021-04-08 13:18:32 -04:00
Northmoc
4a8f299bb3 gifts_ungiven.txt clean up 2021-04-08 12:58:31 -04:00
Northmoc
5530ca94f3 add Mandatory to some searches that need it 2021-04-08 12:46:42 -04:00
Northmoc
61a1b888bd - add ChoiceTitle 2021-04-08 12:43:40 -04:00
Northmoc
a583701319 intuition.txt fix targeting/choosing 2021-04-08 12:35:10 -04:00
Northmoc
57623c710a remove bad ) 2021-04-08 11:12:44 -04:00
Northmoc
93488528de add TriggerZones to Ward 2021-04-08 11:07:54 -04:00
Northmoc
e7a5ffdf5f containment_breach.txt fix ValidTgts 2021-04-08 11:07:16 -04:00
Northmoc
dc56cb382b elspeth_conquers_death.txt improved AI tags 2021-04-08 11:06:26 -04:00
Northmoc
bb22ea3c8f AI logic for Elspeth Conquers Death 2021-04-08 11:05:22 -04:00
CCTV-1
b856ad0374 translate learnLesson message. 2021-04-08 20:07:56 +08:00
Anthony Calosa
3aff3c51a8 Merge branch 'kevlahnota-master-patch-34682' into 'master'
Update CEditorConstructed.java

See merge request core-developers/forge!4431
2021-04-08 11:09:38 +00:00
Anthony Calosa
3e096a25f7 Update CEditorConstructed.java 2021-04-08 11:09:16 +00:00
Anthony Calosa
adb21e4a7e Merge branch 'c21_edition_update' into 'master'
Update C21 Edition file

See merge request core-developers/forge!4430
2021-04-08 11:03:38 +00:00
paul_snoops
3ea268aea7 Update C21 Edition file 2021-04-08 10:05:54 +01:00
Michael Kamensky
567a8913e4 Merge branch 'Williams-master-patch-18379' into 'master'
C21 07/04 Suthro

See merge request core-developers/forge!4426
2021-04-08 08:05:48 +00:00
John
fd89b30192 Update spawning_kraken.txt 2021-04-08 06:30:32 +00:00
John
ce3b327cff Update guardian_augmenter.txt 2021-04-08 06:29:55 +00:00
John
4e5ac4a921 Update curiosity_crafter.txt 2021-04-08 06:29:23 +00:00
Michael Kamensky
b44c1478b8 Merge branch 'c21_7' into 'master'
C21 - 7 April

See merge request core-developers/forge!4422
2021-04-08 04:10:42 +00:00
Michael Kamensky
b0890ad4bf Merge branch 'resolvedThisTurn' into 'master'
Card: add AbilityResolvedThisTurn

See merge request core-developers/forge!4427
2021-04-08 04:09:44 +00:00
Anthony Calosa
83d632e24c Merge branch 'master' into 'master'
update preload itempool

See merge request core-developers/forge!4428
2021-04-08 02:35:07 +00:00
Anthony Calosa
1e5874aed5 update preload itempool 2021-04-08 09:57:26 +08:00
Northmoc
e807d9a3e7 support for playerXCount TotalCommanderCastFromCommandZone 2021-04-07 21:22:21 -04:00
Northmoc
17bb74ff25 commanders_insight.txt 2021-04-07 21:19:44 -04:00
Hans Mackowiak
5c9e2839eb Card: add AbilityResolvedThisTurn 2021-04-08 02:13:45 +02:00
John
d670a761d9 Update paradox_zone.txt 2021-04-07 22:47:42 +00:00
John
b6a3f765a7 Update paradox_zone.txt 2021-04-07 22:42:30 +00:00
John
2ca2ceea5a Update sequence_engine.txt 2021-04-07 22:41:58 +00:00
John
01b4ab512c Update spawning_kraken.txt 2021-04-07 22:41:33 +00:00
John
d18a6c3479 Update theoretical_duplication.txt 2021-04-07 22:40:59 +00:00
John
ab931cdcb4 Update curiosity_crafter.txt 2021-04-07 22:40:03 +00:00
John
7252b0e69a Add new file 2021-04-07 22:39:04 +00:00
John
ab0ed23c83 Add new file 2021-04-07 22:38:21 +00:00
John
18c002094f Add new file 2021-04-07 22:37:04 +00:00
John
d5f73cb633 Add new file 2021-04-07 22:36:15 +00:00
John
c738da065e Add new file 2021-04-07 22:35:30 +00:00
John
a9296d67f7 Add new file 2021-04-07 22:34:40 +00:00
John
78cb417cfc Add new file 2021-04-07 22:34:00 +00:00
John
4bec5f6792 Add new file 2021-04-07 22:33:14 +00:00
John
3da03f66e0 Add new file 2021-04-07 22:31:33 +00:00
Northmoc
e379c676d0 muse_vortex.txt 2021-04-07 16:58:29 -04:00
Bug Hunter
6565494781 Merge branch 'TRT-master-patch-82622' into 'master'
Update forge-gui/res/cardsfolder/upcoming/abundant_harvest.txt

See merge request core-developers/forge!4425
2021-04-07 20:45:09 +00:00
Bug Hunter
aafcd25403 Update forge-gui/res/cardsfolder/upcoming/abundant_harvest.txt 2021-04-07 20:44:20 +00:00
Northmoc
72c4c0007f oversimplify.txt fix Descs 2021-04-07 15:55:36 -04:00
Northmoc
fd1ce7a750 geometric_nexus.txt 2021-04-07 15:53:26 -04:00
Northmoc
fbbdec5e4e oversimplify.txt 2021-04-07 15:33:51 -04:00
Northmoc
443b613513 deekah_fractal_theorist.txt remove default params 2021-04-07 15:05:05 -04:00
Michael Kamensky
2fa44fc0f4 Merge branch 'Williams-master-patch-07577' into 'master'
C21 Discord Contributions 06/04

See merge request core-developers/forge!4410
2021-04-07 18:02:04 +00:00
Northmoc
5ed21c3ce3 deekah_fractal_theorist.txt 2021-04-07 13:16:16 -04:00
Northmoc
f1d52fb530 ruxa_patient_professor.txt (Suthro) 2021-04-07 12:59:37 -04:00
Northmoc
d418ad8369 fractal_harness.txt 2021-04-07 12:46:48 -04:00
Bug Hunter
7b58d8e77e Merge branch 'TRT-master-patch-33908' into 'master'
Update forge-gui/res/cardsfolder/upcoming/fractal_summoning.txt

See merge request core-developers/forge!4423
2021-04-07 16:31:09 +00:00
Bug Hunter
38971c2946 Update forge-gui/res/cardsfolder/upcoming/fractal_summoning.txt 2021-04-07 16:30:10 +00:00
Northmoc
6cef7fd6b6 creative_technique.txt 2021-04-07 11:14:38 -04:00
Northmoc
92ffb0b1e5 rionya_fire_dancer.txt 2021-04-07 10:44:21 -04:00
Northmoc
75f03f1fd1 support for SumCMCGraveyard (Inferno Project) 2021-04-07 10:30:01 -04:00
Northmoc
0306817ac8 inferno_project.txt 2021-04-07 10:29:14 -04:00
Tim Mocny
03b5dc046d Merge branch 'cmc' into 'master'
fix cmc necrotic_fumes.txt

See merge request core-developers/forge!4421
2021-04-07 13:32:42 +00:00
Northmoc
bb5cb8a773 necrotic_fumes.txt 2021-04-07 09:31:40 -04:00
Sol
719bd257a6 Merge branch 'retriever_phoenix_fix' into 'master'
Retriever Phoenix fix for the following error

See merge request core-developers/forge!4420
2021-04-07 12:08:59 +00:00
paul_snoops
e21257ab71 Retriever Phoenix fix for the following error
Caused by: java.lang.RuntimeException: AbilityFactory : getAbility -- Retriever Phoenix has no SVar: TrigLearn
2021-04-07 12:39:41 +01:00
Bug Hunter
fc933ba9d5 Merge branch 'TRT-master-patch-18852' into 'master'
Update forge-gui/res/cardsfolder/upcoming/basic_conjuration.txt

See merge request core-developers/forge!4419
2021-04-07 09:32:28 +00:00
Bug Hunter
9f9215cb3f Update forge-gui/res/cardsfolder/upcoming/basic_conjuration.txt 2021-04-07 09:31:36 +00:00
Anthony Calosa
8308c21d39 Merge branch 'kevlahnota-master-patch-67446' into 'master'
Update StaticData.java

See merge request core-developers/forge!4418
2021-04-07 08:00:16 +00:00
Anthony Calosa
1f6ef5f045 Update StaticData.java 2021-04-07 07:59:39 +00:00
Anthony Calosa
7673918178 Merge branch 'master' into 'master'
Update support for Custom Cards

See merge request core-developers/forge!4417
2021-04-07 07:53:23 +00:00
Anthony Calosa
92b0aad937 Update support for Custom Cards 2021-04-07 15:50:24 +08:00
Michael Kamensky
233e126cd3 Merge branch 'Williams-master-patch-47951' into 'master'
Update Judge Gift Cards 2021.txt

See merge request core-developers/forge!4416
2021-04-07 07:33:50 +00:00
John
e9506787d5 Update Judge Gift Cards 2021.txt 2021-04-07 06:51:51 +00:00
John
e061289a26 Update elementalists_palette.txt 2021-04-07 06:47:38 +00:00
Michael Kamensky
7f87164235 Merge branch 'learnEffect' into 'master'
Learn effect

See merge request core-developers/forge!4374
2021-04-07 05:11:55 +00:00
Hans Mackowiak
0627d37f69 - Basic AI for AF Learn
- Some minor tweaks and fixes for AF Learn
2021-04-07 05:11:55 +00:00
Michael Kamensky
68dc49cb88 Merge branch 'secretly' into 'master'
Emissary of Grudges, Stalking Leonin

See merge request core-developers/forge!4413
2021-04-07 05:11:28 +00:00
Michael Kamensky
24cf97ca84 Merge branch 'cultic' into 'master'
Add Cultic Cube

See merge request core-developers/forge!4411
2021-04-07 05:08:13 +00:00
Michael Kamensky
9859cbdca6 Merge branch 'ConditionDefinedSpells' into 'master'
SpellAbilityCondition: extends ConditionDefined to work with SpellAbilities

See merge request core-developers/forge!4412
2021-04-07 05:07:58 +00:00
Michael Kamensky
b819f303bb Merge branch 'new-cards' into 'master'
C21 Cards

See merge request core-developers/forge!4414
2021-04-07 05:07:18 +00:00
Michael Kamensky
5ad4a25c33 Merge branch 'card-fixes' into 'master'
Some card fixes

See merge request core-developers/forge!4415
2021-04-07 05:07:01 +00:00
Adam Pantel
265e5bb122 Golden Ratio fix 2021-04-06 23:35:18 -04:00
Adam Pantel
7964f862c6 Fixes from discord 2021-04-06 22:49:16 -04:00
Adam Pantel
622176d3de Surge to Victory 2021-04-06 22:43:51 -04:00
Adam Pantel
7e793a48e7 Duration fix on Rowan 2021-04-06 22:18:58 -04:00
Adam Pantel
5bdc4615e8 Reinterpret 2021-04-06 19:31:39 -04:00
Adam Pantel
ef04a4376a Emissary of Grudges, Stalking Leonin 2021-04-06 19:29:37 -04:00
John
3c57007765 Update rousing_refrain.txt 2021-04-06 22:08:21 +00:00
John
9965f2e79c Update inspiring_refrain.txt 2021-04-06 22:06:13 +00:00
John
f131997d6a Update sly_instigator.txt 2021-04-06 22:05:02 +00:00
John
08b9e3c46a Update inspiring_refrain.txt 2021-04-06 22:04:08 +00:00
John
8afbaefa6d Update inspiring_refrain.txt 2021-04-06 22:00:21 +00:00
John
88a8336ae7 Update rousing_refrain.txt 2021-04-06 21:59:41 +00:00
John
0c6dface18 Update rousing_refrain.txt 2021-04-06 21:59:01 +00:00
John
9bb2dd3887 Update inspiring_refrain.txt 2021-04-06 21:49:48 +00:00
John
1f3943fe9a Update octavia_living_thesis.txt 2021-04-06 21:48:59 +00:00
John
9502034d58 Update inspiring_refrain.txt 2021-04-06 21:48:37 +00:00
Hans Mackowiak
c6b0b2021c SpellAbilityCondition: extends ConditionDefined to work with SpellAbilities 2021-04-06 22:13:48 +02:00
John
0d889ec773 Add new file 2021-04-06 19:55:33 +00:00
John
3d8f848936 Add new file 2021-04-06 19:54:52 +00:00
Adam Pantel
6cbd108bca Add Cultic Cube 2021-04-06 15:03:26 -04:00
John
03c950f82e Add new file 2021-04-06 17:58:35 +00:00
John
552b061357 Add new file 2021-04-06 17:47:44 +00:00
John
ca98432e20 Add new file 2021-04-06 17:21:44 +00:00
John
6ada1f04da Add new file 2021-04-06 17:20:43 +00:00
John
edf1738e3c Add new file 2021-04-06 17:18:45 +00:00
Michael Kamensky
5b77205b77 Merge branch 'ranking' into 'master'
Add default cube rankings from cubecobra

See merge request core-developers/forge!4409
2021-04-06 17:09:07 +00:00
Michael Kamensky
67128c2fd5 Merge branch 'new-cards' into 'master'
C21 Cards

See merge request core-developers/forge!4406
2021-04-06 17:08:49 +00:00
Michael Kamensky
490ec7ef3e Merge branch 'Williams-master-patch-25580' into 'master'
C21 Suthro 5/4

See merge request core-developers/forge!4401
2021-04-06 17:08:27 +00:00
Michael Kamensky
0ab7ff919b Merge branch 'new-cards-2' into 'master'
Demonstrate keyword and Excavation Technique

See merge request core-developers/forge!4397
2021-04-06 17:08:12 +00:00
Adam Pantel
489ccd9003 Add default cube rankings from cubecobra 2021-04-06 12:28:29 -04:00
Sol
9971eef9b8 Merge branch 'Williams-master-patch-65571' into 'master'
Update STX edition file

See merge request core-developers/forge!4407
2021-04-06 14:43:38 +00:00
John
88e8bed395 Update Standard.txt 2021-04-06 14:29:27 +00:00
John
9304942fd6 Update Pioneer.txt 2021-04-06 14:29:14 +00:00
John
5f3b5cbe26 Update Modern.txt 2021-04-06 14:28:42 +00:00
John
c5dbc8ddca Update Strixhaven School of Mages.txt 2021-04-06 14:07:29 +00:00
John
eaea041e73 Update blocks.txt 2021-04-06 14:04:16 +00:00
John
20b4ae230f Update Strixhaven School of Mages.txt 2021-04-06 14:02:56 +00:00
Adam Pantel
037dce8be8 Wake the Past 2021-04-06 09:59:08 -04:00
Anthony Calosa
3ad6045377 Merge branch 'master' into 'master'
update AssetsDownloader

See merge request core-developers/forge!4405
2021-04-06 12:18:40 +00:00
Anthony Calosa
3f683c3909 update AssetsDownloader 2021-04-06 20:16:45 +08:00
Anthony Calosa
2f9974f6ef Merge branch 'master' into 'master'
fix downloader

See merge request core-developers/forge!4404
2021-04-06 11:25:47 +00:00
Anthony Calosa
d2156a1179 Merge remote-tracking branch 'core/master' 2021-04-06 19:24:33 +08:00
Anthony Calosa
d6a0b92f1e fix downloader 2021-04-06 19:21:45 +08:00
Bug Hunter
15aab58415 Merge branch 'TRT-master-patch-76250' into 'master'
Update forge-gui/res/cardsfolder/upcoming/culling_ritual.txt

See merge request core-developers/forge!4403
2021-04-06 10:40:56 +00:00
Bug Hunter
1e8679bf94 Update forge-gui/res/cardsfolder/upcoming/culling_ritual.txt 2021-04-06 10:39:39 +00:00
Michael Kamensky
4eeb4c515f Merge branch 'fixtarget' into 'master'
DestroyAI: Fix illegal targets

See merge request core-developers/forge!4394
2021-04-06 08:57:17 +00:00
John
76a75942a9 Update triplicate_titan.txt 2021-04-06 08:47:17 +00:00
John
4383c3486a Update digsite_engineer.txt 2021-04-06 08:46:53 +00:00
Anthony Calosa
e629877984 Merge branch 'master' into 'master'
fix filteredCards, fix NPE devmode

See merge request core-developers/forge!4402
2021-04-06 07:53:23 +00:00
Anthony Calosa
fb71ea83f0 Merge remote-tracking branch 'core/master' 2021-04-06 15:50:39 +08:00
Anthony Calosa
d9cf090060 update 2021-04-06 15:49:30 +08:00
Michael Kamensky
a0fd16513a Merge branch 'c21_5' into 'master'
C21 - 5 April

See merge request core-developers/forge!4396
2021-04-06 07:30:26 +00:00
Michael Kamensky
c7c6d4e162 Merge branch 'new-cards' into 'master'
Strict Proctor

See merge request core-developers/forge!4393
2021-04-06 07:27:15 +00:00
Anthony Calosa
9a2eaa7a1d fix filteredCards, fix NPE devmode 2021-04-06 15:27:14 +08:00
Michael Kamensky
3b12843d5f Merge branch 'card-fixes' into 'master'
Typo in Liliana's Scorn

See merge request core-developers/forge!4398
2021-04-06 07:25:20 +00:00
Michael Kamensky
6685649e39 Merge branch 'AIplayCantBeCast' into 'master'
AI: Add missing CantBeCast check from PlayEffect

See merge request core-developers/forge!4391
2021-04-06 07:23:55 +00:00
John
7e63c902d3 Update triplicate_titan.txt 2021-04-06 06:50:50 +00:00
John
c1e1530cd9 Update losheel_clockwork_scholar.txt 2021-04-06 06:49:52 +00:00
John
b1a190093c Update digsite_engineer.txt 2021-04-06 06:49:01 +00:00
Hans Mackowiak
fcc7cf0566 Merge branch 'kevlahnota-master-patch-51370' into 'master'
Update lathiel_the_bounteous_dawn.txt

See merge request core-developers/forge!4400
2021-04-06 06:47:33 +00:00
Anthony Calosa
ca3d51157f Update lathiel_the_bounteous_dawn.txt 2021-04-06 06:47:33 +00:00
John
899a7b2500 Update bronze_guardian.txt 2021-04-06 06:46:58 +00:00
John
d19be1731e Update angel_of_ruins.txt 2021-04-06 06:45:26 +00:00
John
90768b6933 Update alibou_ancient_witness.txt 2021-04-06 06:43:39 +00:00
John
89d6f050d1 Add new file 2021-04-06 06:40:06 +00:00
John
5bfe7a2146 Add new file 2021-04-06 06:39:07 +00:00
John
e54dc157ac Add new file 2021-04-06 06:38:17 +00:00
John
5be7472dd9 Add new file 2021-04-06 06:37:16 +00:00
John
1a2de2bbc2 Add new file 2021-04-06 06:36:01 +00:00
John
0c9935b1b5 Add new file 2021-04-06 06:34:55 +00:00
John
aba00ad17b Add new file 2021-04-06 06:33:04 +00:00
Anthony Calosa
5ca05394d6 Merge branch 'kevlahnota-master-patch-58460' into 'master'
Update ImageCache.java

See merge request core-developers/forge!4399
2021-04-06 03:59:42 +00:00
Anthony Calosa
5165415fb1 Update ImageCache.java
Don't dispose defaultImage
2021-04-06 03:59:21 +00:00
Adam Pantel
1f1060b97c Typo in Liliana's Scorn 2021-04-05 23:50:37 -04:00
Adam Pantel
3e9dabe2a5 Demonstrate keyword and Excavation Technique 2021-04-05 23:19:33 -04:00
Northmoc
86d72b15c5 support for archaeomancers_map.txt 2021-04-05 23:05:56 -04:00
Northmoc
3fd3a3e5aa archaeomancers_map.txt 2021-04-05 23:05:16 -04:00
Northmoc
3fbcf09326 ruin_grinder.txt 2021-04-05 23:04:47 -04:00
Anthony Calosa
d97cd9eabe Merge branch 'STX_edition' into 'master'
STX edition missing cards

See merge request core-developers/forge!4392
2021-04-06 01:32:58 +00:00
Sol
de52bb1036 Merge branch 'card-fixes' into 'master'
MoJhoSto improvements

See merge request core-developers/forge!4395
2021-04-06 01:19:38 +00:00
Adam Pantel
f9a073fd93 Momir script improvement 2021-04-05 19:32:51 -04:00
Adam Pantel
c77f99cf9b MoJhoSto logging 2021-04-05 18:57:36 -04:00
Northmoc
af8c1b2092 cursed_mirror.txt 2021-04-05 18:45:59 -04:00
Adam Pantel
9095dcd9d8 Add infinite loop protection for Stonehewer 2021-04-05 18:21:32 -04:00
Northmoc
08359103da teachings_of_the_archaics.txt only 3 cards 2021-04-05 18:15:36 -04:00
Northmoc
9fe15fd8c2 audacious_reshapers.txt 2021-04-05 18:11:18 -04:00
Northmoc
728afbc887 madcap_experiment.txt tidy up 2021-04-05 18:10:51 -04:00
Adam Pantel
1e10d49d0f Strict Proctor 2021-04-05 17:51:26 -04:00
tool4EvEr
4a81ea0ee4 Fix illegal targets 2021-04-05 23:50:16 +02:00
Northmoc
acf89b1e9e monologue_tax.txt 2021-04-05 17:41:01 -04:00
paul_snoops
1c99b5bbc7 STX edition missing cards 2021-04-05 22:11:33 +01:00
Northmoc
d8d0c67462 study_hall.txt 2021-04-05 16:29:22 -04:00
Michael Kamensky
2e9f050707 Merge branch 'stx_5' into 'master'
STX - Conspiracy Theorist

See merge request core-developers/forge!4387
2021-04-05 19:29:30 +00:00
Michael Kamensky
4b0a3741de Merge branch 'livio' into 'master'
livio_oathsworn_sentinel.txt fixup

See merge request core-developers/forge!4390
2021-04-05 19:28:27 +00:00
Michael Kamensky
c45c5f6e92 Merge branch 'new-cards-2' into 'master'
Dragon's Approach

See merge request core-developers/forge!4363
2021-04-05 19:26:54 +00:00
Michael Kamensky
482515f538 Merge branch 'fixdig' into 'master'
Fix DigMultiple not changing cards when no valid targets

See merge request core-developers/forge!4386
2021-04-05 19:26:16 +00:00
tool4EvEr
42d75bd1ee Add missing CantBeCast check 2021-04-05 21:06:52 +02:00
Northmoc
296d7436ae fix stonehewer_giant_avatar.txt 2021-04-05 12:38:39 -04:00
Northmoc
5ba1466b96 livio_oathsworn_sentinel.txt fixup 2021-04-05 12:02:43 -04:00
Sol
7cdf36df87 Merge branch 'STX_fixes' into 'master'
STX edition fix MDFCs

See merge request core-developers/forge!4389
2021-04-05 14:57:07 +00:00
paul_snoops
02f25f8816 STX edition fix MDFCs 2021-04-05 15:54:13 +01:00
Sol
fa7e6bfa36 Merge branch 'stx_update' into 'master'
STX edition file update

See merge request core-developers/forge!4367
2021-04-05 14:07:13 +00:00
paul_snoops
854890cc77 STX edition file update 2021-04-05 14:57:15 +01:00
Northmoc
5445ec1b8d RememberTriggerCards param support for Conspiracy Theorist 2021-04-05 08:44:40 -04:00
Northmoc
050f11bfab conspiracy_theorist.txt 2021-04-05 08:43:46 -04:00
tool4EvEr
da89b95654 Fix DigMultiple not changing cards when no valid targets 2021-04-05 13:41:46 +02:00
Anthony Calosa
71631ccf0c Merge branch 'churrufli-master-patch-15537' into 'master'
Update text, resource file size

See merge request core-developers/forge!4384
2021-04-05 10:24:05 +00:00
Churrufli
f1c38dd742 Update text 2021-04-05 10:01:34 +00:00
Bug Hunter
0fc7126238 Merge branch 'typofix' into 'master'
Show of Confidence: Fix cost typo

See merge request core-developers/forge!4383
2021-04-05 09:45:53 +00:00
tool4EvEr
41cf366014 Fix cost typo 2021-04-05 11:45:13 +02:00
Anthony Calosa
4288ae3582 Merge branch 'master' into 'master'
[Mobile] Seperate cache for Cards and Other Images

See merge request core-developers/forge!4382
2021-04-05 09:07:03 +00:00
Anthony Calosa
e57d0a4d0e [Mobile] add OtherImageLoader for otherCache 2021-04-05 17:04:40 +08:00
Anthony Calosa
1b6a0188f3 [Mobile] Seperate cache for Cards/Tokens and Icon/Booster/Fatpacks 2021-04-05 15:45:22 +08:00
Michael Kamensky
e225b8a77c Merge branch 'card-fixes' into 'master'
Codie oracle text

See merge request core-developers/forge!4381
2021-04-05 04:12:40 +00:00
Michael Kamensky
e4b1c542db Merge branch 'sheetfix' into 'master'
Sheets / Edition parsing fixes

See merge request core-developers/forge!4377
2021-04-05 04:12:33 +00:00
Adam Pantel
b960460e02 Codie oracle text 2021-04-04 23:33:31 -04:00
Anthony Calosa
c8f39802ae Merge branch 'master' into 'master'
Limit preloading

See merge request core-developers/forge!4380
2021-04-05 03:27:59 +00:00
Anthony Calosa
e3caad856d Limit preloading 2021-04-05 11:25:42 +08:00
Anthony Calosa
97bc512d1c Merge branch 'master' into 'master'
cleanup

See merge request core-developers/forge!4379
2021-04-05 02:07:51 +00:00
Anthony Calosa
5752fe698f cleanup 2021-04-05 10:06:16 +08:00
Anthony Calosa
727ef87a11 Merge branch 'master' into 'master'
[Mobile] Update ImageCache Texture dispose

See merge request core-developers/forge!4378
2021-04-04 22:56:04 +00:00
Anthony Calosa
a92c85a0be [Mobile] Update ImageCache Texture dispose 2021-04-05 06:51:27 +08:00
tool4EvEr
225790cc4b Fix parsing of new edition sections 2021-04-04 23:52:41 +02:00
tool4EvEr
0202d52e49 Printsheets: Update wrong JMP codes 2021-04-04 22:56:09 +02:00
Bug Hunter
397ad4c597 Merge branch 'brawl2' into 'master'
Add commander relocation for brawl

See merge request core-developers/forge!4376
2021-04-04 20:29:12 +00:00
tool4EvEr
ce00f31f0f Add commander relocation for brawl 2021-04-04 22:28:19 +02:00
swordshine
b9dc9e6eb0 Merge branch 'master' into 'master'
Leonin Vanguard fix + Medomai

See merge request core-developers/forge!4372
2021-04-04 15:33:30 +00:00
Anthony Calosa
05884bba0d Merge branch 'master' into 'master'
[Mobile] When generating a lot of image at once (more than 100) in the shop...

See merge request core-developers/forge!4373
2021-04-04 13:40:36 +00:00
Anthony Calosa
d8f37ac6a7 Merge remote-tracking branch 'core/master' 2021-04-04 21:38:01 +08:00
Anthony Calosa
9c89e0b790 [Mobile] When generating a lot of image at once (more than 100) in the shop list/rewards/unlocked sets, clear the cache 2021-04-04 21:35:01 +08:00
Hythonia
b37d695c8b Minor card fixes 2021-04-04 15:06:50 +02:00
Michael Kamensky
263db54a12 Merge branch 'Williams-master-patch-50535' into 'master'
STX Discord Contributions

See merge request core-developers/forge!4362
2021-04-04 11:23:18 +00:00
Michael Kamensky
dc8c25b8bc Merge branch 'master' into 'master'
STX: Last three cards

See merge request core-developers/forge!4365
2021-04-04 11:18:53 +00:00
Anthony Calosa
1d43cae1b1 Merge branch 'kevlahnota-master-patch-75509' into 'master'
unused import

See merge request core-developers/forge!4371
2021-04-04 10:58:35 +00:00
Anthony Calosa
7e02ae518d unused import 2021-04-04 10:58:14 +00:00
Bug Hunter
4afc439eb1 Merge branch 'Williams-master-patch-14604' into 'master'
Update make_your_mark.txt

See merge request core-developers/forge!4369
2021-04-04 10:39:19 +00:00
Anthony Calosa
330d4f006f Merge branch 'kevlahnota-master-patch-65912' into 'master'
update token-images

See merge request core-developers/forge!4370
2021-04-04 10:23:17 +00:00
Anthony Calosa
5929a7c6f9 Update token-images.txt 2021-04-04 10:19:57 +00:00
Anthony Calosa
1b95989b05 Update token-images.txt 2021-04-04 10:14:59 +00:00
John
b284fc590e Update make_your_mark.txt 2021-04-04 10:13:45 +00:00
Anthony Calosa
dd3e21e5d4 Merge branch 'kevlahnota-master-patch-92499' into 'master'
Update ImageCache.java

See merge request core-developers/forge!4368
2021-04-04 09:29:00 +00:00
Anthony Calosa
31c674c3dc Update ImageCache.java 2021-04-04 09:28:15 +00:00
paul_snoops
b3632417a7 STX edition file update (no tokens yet) 2021-04-04 10:24:35 +01:00
Anthony Calosa
bc28919727 Merge branch 'kevlahnota-master-patch-57420' into 'master'
Update borderlessCardList.txt

See merge request core-developers/forge!4366
2021-04-04 09:12:05 +00:00
Anthony Calosa
4333744248 Update borderlessCardList.txt 2021-04-04 09:11:20 +00:00
John
d62599d62c Add new file 2021-04-04 07:31:19 +00:00
John
bad745d2a9 Add new file 2021-04-04 07:30:02 +00:00
Hythonia
0c6aa6afdb STX: Last three cards 2021-04-04 09:29:37 +02:00
John
bb8e68d1b1 Add new file 2021-04-04 07:29:31 +00:00
John
8c6b6e617c Add new file 2021-04-04 07:29:01 +00:00
John
a9e4270ddb Add new file 2021-04-04 07:28:24 +00:00
John
72262a77fd Add new file 2021-04-04 07:27:47 +00:00
John
68c524a75d Add new file 2021-04-04 07:27:08 +00:00
John
64c56288c9 Add new file 2021-04-04 07:26:25 +00:00
John
2996bf7e6b Add new file 2021-04-04 07:25:46 +00:00
John
5520c3778b Add new file 2021-04-04 07:23:42 +00:00
John
4c6bdcb68a Add new file 2021-04-04 07:23:08 +00:00
John
a925786899 Add new file 2021-04-04 07:22:18 +00:00
John
e73a3cbf8c Add new file 2021-04-04 07:21:26 +00:00
John
c86dbb4665 Add new file 2021-04-04 07:20:24 +00:00
John
951c338edd Add new file 2021-04-04 07:19:29 +00:00
John
5a22ad86b8 Add new file 2021-04-04 07:18:25 +00:00
John
6db22d43a6 Add new file 2021-04-04 07:17:26 +00:00
John
6569f59501 Add new file 2021-04-04 07:16:44 +00:00
John
c30b04131b Add new file 2021-04-04 07:15:58 +00:00
John
fb7e6d482a Add new file 2021-04-04 07:15:13 +00:00
John
df252a0051 Add new file 2021-04-04 07:14:08 +00:00
John
443d79f902 Update springmane_cervin.txt 2021-04-04 07:11:38 +00:00
John
7ac9997950 Update emergent_sequence.txt 2021-04-04 07:10:58 +00:00
John
aa59b5b4e3 Update callous_bloodmage.txt 2021-04-04 07:10:29 +00:00
John
2cd0b56f11 Update acess_tunnel.txt 2021-04-04 07:09:45 +00:00
Michael Kamensky
40c8485f02 Merge branch 'fix_illusionary_mask' into 'master'
Copy SA without mana cost will also copy castFaceDown flag. (Fix for Illusionary Mask)

See merge request core-developers/forge!4364
2021-04-04 04:26:17 +00:00
Michael Kamensky
f70e22fa4d Merge branch 'biblio' into 'master'
STX - The Biblioplex

See merge request core-developers/forge!4361
2021-04-04 04:25:56 +00:00
Michael Kamensky
cbaa7028d0 Merge branch 'stx_3' into 'master'
STX - 3 April

See merge request core-developers/forge!4360
2021-04-04 04:25:49 +00:00
Michael Kamensky
d6a4e33035 Merge branch 'new-cards' into 'master'
STX Cards

See merge request core-developers/forge!4353
2021-04-04 04:25:14 +00:00
Adam Pantel
99bae93e07 STX Cards 2021-04-03 23:37:21 -04:00
Lyu Zong-Hong
9097fb6f60 Copy SA without mana cost will also copy castFaceDown flag. (Fix for Illusionary Mask) 2021-04-04 10:43:55 +09:00
John
8e9b91eeb6 Add new file 2021-04-03 23:22:06 +00:00
John
419b46c7cc Add new file 2021-04-03 23:21:14 +00:00
John
e927d76cb5 Add new file 2021-04-03 23:20:19 +00:00
John
966d190b77 Add new file 2021-04-03 23:19:37 +00:00
John
b7b06413d6 Add new file 2021-04-03 23:18:54 +00:00
John
67ba5e21f7 Add new file 2021-04-03 23:17:51 +00:00
John
27e401d3a1 Add new file 2021-04-03 23:17:04 +00:00
John
45c1c40e9f Add new file 2021-04-03 23:16:21 +00:00
John
29cfde5249 Upload New File 2021-04-03 23:00:13 +00:00
John
ffecb391b3 Upload New File 2021-04-03 22:59:44 +00:00
John
c87f8b0986 Add new file 2021-04-03 22:35:13 +00:00
John
8cc4eef916 Add new file 2021-04-03 22:34:05 +00:00
John
46f241f004 Add new file 2021-04-03 22:33:18 +00:00
John
4dbe0bdbcb Add new file 2021-04-03 22:32:09 +00:00
John
ee5e0beb7f Add new file 2021-04-03 22:30:56 +00:00
John
ef89a35de6 Add new file 2021-04-03 22:30:14 +00:00
Adam Pantel
26f94ccf07 Dragon's Approach 2021-04-03 18:29:57 -04:00
John
c7a0b9d31c Add new file 2021-04-03 22:29:13 +00:00
John
e2f91339b4 Add new file 2021-04-03 22:28:23 +00:00
John
155b2723f1 Add new file 2021-04-03 22:27:34 +00:00
John
2b5d5c5c12 Add new file 2021-04-03 22:26:22 +00:00
John
660f854546 Add new file 2021-04-03 22:25:42 +00:00
John
505ae78d14 Add new file 2021-04-03 22:25:02 +00:00
John
0f19934021 Add new file 2021-04-03 22:23:29 +00:00
John
0865e3262f Add new file 2021-04-03 22:22:46 +00:00
John
8d266dbc4c Add new file 2021-04-03 22:22:00 +00:00
John
8801365896 Add new file 2021-04-03 22:21:11 +00:00
John
33249ea526 Add new file 2021-04-03 22:20:26 +00:00
John
d1f3ec25fc Add new file 2021-04-03 22:18:08 +00:00
Northmoc
8ea76ee1b8 pestilent_cauldron_restorative_burst.txt 2021-04-03 15:28:29 -04:00
Michael Kamensky
660ed4c341 Merge branch 'Williams-master-patch-56008' into 'master'
STX Contibutions

See merge request core-developers/forge!4357
2021-04-03 19:00:05 +00:00
Northmoc
ef6f88a129 dramatic_finale.txt 2021-04-03 14:45:46 -04:00
Northmoc
bf072e0802 move TriggerZone param 2021-04-03 14:30:29 -04:00
John
d910521a0b Update zimone_quandrix_prodigy.txt 2021-04-03 18:22:43 +00:00
John
3ceb40741c Update zimone_quandrix_prodigy.txt 2021-04-03 18:22:15 +00:00
John
7d3a192435 Update witherbloom_pledgemage.txt 2021-04-03 18:21:39 +00:00
John
50af4b83c5 Update witherbloom_pledgemage.txt 2021-04-03 18:20:43 +00:00
John
2e4f79f6ef Update twinscroll_shaman.txt 2021-04-03 18:20:17 +00:00
John
7c57be2181 Update test_of_talents.txt 2021-04-03 18:19:52 +00:00
John
2f238789ad Update professors_warning.txt 2021-04-03 18:19:17 +00:00
John
29d4cfe669 Update professor_of_zoomancy.txt 2021-04-03 18:18:51 +00:00
John
df4eaa28b9 Update mila_crafty_companion_lukka_wayward_bonder.txt 2021-04-03 18:18:23 +00:00
John
42dc55c664 Update mila_crafty_companion_lukka_wayward_bonder.txt 2021-04-03 18:16:37 +00:00
John
a82ad37e78 Update mila_crafty_companion_lukka_wayward_bonder.txt 2021-04-03 18:15:45 +00:00
John
5fdc7970cd Update draconic_intervention.txt 2021-04-03 18:15:12 +00:00
John
8f45cc62d2 Update draconic_intervention.txt 2021-04-03 18:14:36 +00:00
Northmoc
b8eaa369c2 add AI hint 2021-04-03 13:57:34 -04:00
Northmoc
fec2a4fcd5 blot_out_the_sky.txt (Suthro) 2021-04-03 13:57:33 -04:00
Northmoc
f40ca2037b rushed_rebirth.txt (Suthro) 2021-04-03 13:57:33 -04:00
Northmoc
7227885e0e silverquill_silencer.txt (Suthro) 2021-04-03 13:57:32 -04:00
Northmoc
4fe66ddca8 add NonLegendary param to copySpellHost (for Double Major) 2021-04-03 13:57:32 -04:00
Northmoc
5472f17c96 double_major.txt 2021-04-03 13:57:32 -04:00
Northmoc
69b072f130 augmenter_pugilist_echoing_equation.txt 2021-04-03 13:57:31 -04:00
Northmoc
640106a78a jadzi_oracle_of_arcavios_journey_to_the_oracle.txt 2021-04-03 13:57:31 -04:00
Northmoc
2e31e1840a improve awkward prompt 2021-04-03 13:11:45 -04:00
Northmoc
09334d2043 support for OrActivationCardsInHand param 2021-04-03 13:10:09 -04:00
Northmoc
b52d22fd76 the_biblioplex.txt 2021-04-03 13:07:38 -04:00
Michael Kamensky
34233bf682 Merge branch 'stx_2' into 'master'
STX - 2 April

See merge request core-developers/forge!4354
2021-04-03 13:53:57 +00:00
John
3ca111f6e0 Update dina_soul_steeper.txt 2021-04-03 13:16:06 +00:00
John
09556cc3ac Update daemogoth_titan.txt 2021-04-03 13:15:38 +00:00
John
ff94d85e53 Update bookwurm.txt 2021-04-03 13:14:52 +00:00
John
dd759d62e9 Update beledros_witherbloom.txt 2021-04-03 13:13:37 +00:00
Northmoc
9739ed6b87 fix Oracle typo 2021-04-03 08:08:01 -04:00
John
3d4c88ae7c Update tempted_by_the_oriq.txt 2021-04-03 11:03:21 +00:00
John
eb5f842e7b Update zimone_quandrix_prodigy.txt 2021-04-03 10:56:22 +00:00
Anthony Calosa
6fb409dcd0 Merge branch 'kevlahnota-master-patch-68671' into 'master'
Update MagicStack.java

See merge request core-developers/forge!4359
2021-04-03 10:35:04 +00:00
Anthony Calosa
b2e36e8ca2 Update MagicStack.java
Should fix conspire (Wort, the Raidmother), replicate (Djiin Illuminatus)
2021-04-03 10:27:14 +00:00
Anthony Calosa
709524b7c9 Merge branch 'master' into 'master'
Fix spell shop items

See merge request core-developers/forge!4358
2021-04-03 09:32:39 +00:00
Anthony Calosa
4444438670 Merge remote-tracking branch 'core/master' 2021-04-03 17:29:53 +08:00
Anthony Calosa
87ada19569 fix spell shop items 2021-04-03 17:28:58 +08:00
John
082fce33c3 Update witherbloom_pledgemage.txt 2021-04-03 08:01:52 +00:00
John
ae93d2028a Update sedgemoor_witch.txt 2021-04-03 08:00:06 +00:00
John
a36719686b Add new file 2021-04-03 07:52:11 +00:00
John
981dc381cf Add new file 2021-04-03 07:51:26 +00:00
John
261241c1ee Add new file 2021-04-03 07:50:49 +00:00
John
7c8d432aa9 Add new file 2021-04-03 07:49:59 +00:00
John
bd82d5d355 Add new file 2021-04-03 07:49:17 +00:00
John
fed9270211 Add new file 2021-04-03 07:48:28 +00:00
John
2fa0ef2572 Add new file 2021-04-03 07:47:42 +00:00
John
02ffbb2b5c Add new file 2021-04-03 07:46:56 +00:00
John
c00197d44a Add new file 2021-04-03 07:46:13 +00:00
John
aa30390674 Add new file 2021-04-03 07:30:16 +00:00
John
c54bf6f730 Add new file 2021-04-03 07:27:42 +00:00
John
a5cc4cc01d Add new file 2021-04-03 07:26:58 +00:00
John
ca02c342d7 Add new file 2021-04-03 07:25:32 +00:00
John
bd58d81bcc Add new file 2021-04-03 07:24:55 +00:00
John
15dfdb5bc1 Add new file 2021-04-03 07:24:12 +00:00
John
36730dd6e2 Add new file 2021-04-03 07:23:26 +00:00
John
0122589138 Add new file 2021-04-03 07:22:41 +00:00
John
e82b1c4524 Add new file 2021-04-03 07:21:40 +00:00
John
8a0ae86b28 Add new file 2021-04-03 07:20:17 +00:00
John
d406277247 Add new file 2021-04-03 07:19:33 +00:00
John
5e5015c552 Add new file 2021-04-03 07:18:49 +00:00
John
22c46a50f0 Add new file 2021-04-03 07:17:13 +00:00
Michael Kamensky
7c9404864a Merge branch 'issue1789' into 'master'
goblin_welder.txt - improve StackDesc

Closes #1789

See merge request core-developers/forge!4355
2021-04-03 04:46:20 +00:00
Michael Kamensky
82752f55d1 Merge branch 'card-fixes' into 'master'
Fix AltCost NPE

See merge request core-developers/forge!4356
2021-04-03 04:44:36 +00:00
Adam Pantel
50a7643610 Fix AltCost NPE 2021-04-02 21:37:38 -04:00
Northmoc
a85c84096f necrotic_fumes.txt remove extra spaces 2021-04-02 20:33:43 -04:00
Northmoc
7edda12548 selfless_glyphweaver_deadly_vanity.txt 2021-04-02 20:32:47 -04:00
Northmoc
d91f1df9d8 blex_vexing_pest_search_for_blex.txt 2021-04-02 20:05:47 -04:00
Northmoc
7c2f9e529c goblin_welder.txt - improve StackDesc 2021-04-02 19:13:03 -04:00
Northmoc
d7ca06c599 CostAdjustment.java support UnlessValidTarget param for Mavinda 2021-04-02 18:41:42 -04:00
Northmoc
f4ee0441ea mavinda_students_advocate.txt 2021-04-02 18:40:21 -04:00
Michael Kamensky
7b28ea1fa3 Merge branch 'Williams-master-patch-83252' into 'master'
STX More Cards 02/04

See merge request core-developers/forge!4345
2021-04-02 19:10:35 +00:00
John
e02da44288 Add new file 2021-04-02 17:58:40 +00:00
John
e836880a39 Add new file 2021-04-02 17:46:34 +00:00
Anthony Calosa
2ef00808b7 Merge branch 'kevlahnota-master-patch-77063' into 'master'
update, add alias to SLD

See merge request core-developers/forge!4352
2021-04-02 16:36:09 +00:00
Anthony Calosa
1dca52e8cd update, add alias to SLD
Should fix some decks that uses "PSLD" set code before (ie. An unsupported card was requested: "Teferi, Time Raveler" from "PSLD". We're sorry, but you cannot use this card yet.)
2021-04-02 16:34:53 +00:00
John
a4f7bd4d2b Add new file 2021-04-02 16:02:33 +00:00
John
5740917a11 Update promising_duskmage.txt 2021-04-02 14:19:26 +00:00
John
0790220a44 Update novice_dissector.txt 2021-04-02 14:18:57 +00:00
John
462459c6c2 Update karok_wrangler.txt 2021-04-02 14:18:29 +00:00
Michael Kamensky
eee160c379 Merge branch 'gorm' into 'master'
Gorm the Great and support

See merge request core-developers/forge!4349
2021-04-02 14:09:30 +00:00
Michael Kamensky
96c1ce5803 Merge branch 'stackdesc' into 'master'
Use StackDescription for AltCosts

See merge request core-developers/forge!4344
2021-04-02 14:09:03 +00:00
Michael Kamensky
6dc705450b Merge branch 'master' into 'master'
Better fix for CloneAi

See merge request core-developers/forge!4351
2021-04-02 14:08:45 +00:00
Michael Kamensky
e9f5039817 - Better fix for CloneAi 2021-04-02 17:07:07 +03:00
Michael Kamensky
4a7d0222a2 Merge branch 'master' into 'master'
Fix AI logic for ETB CloneAi with Choices

See merge request core-developers/forge!4350
2021-04-02 14:02:43 +00:00
Michael Kamensky
63e7803ea4 - Fix AI logic for ETB CloneAi with Choices 2021-04-02 17:01:59 +03:00
John
c522710f7f Add new file 2021-04-02 12:35:47 +00:00
John
d9be2b2437 Add new file 2021-04-02 12:30:26 +00:00
John
caeaefcc6a Add new file 2021-04-02 12:10:33 +00:00
John
6048161924 Add new file 2021-04-02 12:03:44 +00:00
John
4348bc20eb Add new file 2021-04-02 11:55:49 +00:00
tool4EvEr
bff6d43701 Gorm the Great and support 2021-04-02 13:55:36 +02:00
John
944fc70ff4 Add new file 2021-04-02 11:50:49 +00:00
John
a75a148278 Add new file 2021-04-02 10:35:42 +00:00
John
a378eefb7b Add new file 2021-04-02 10:28:31 +00:00
Bug Hunter
9782705e06 Merge branch 'fixed' into 'master'
Fix typos

See merge request core-developers/forge!4348
2021-04-02 10:26:55 +00:00
tool4EvEr
4cd47da3f4 Fix typos 2021-04-02 12:25:54 +02:00
Bug Hunter
65776d8401 Merge branch 'Williams-master-patch-28392' into 'master'
Fix Arrogant Poet

See merge request core-developers/forge!4347
2021-04-02 10:25:45 +00:00
John
1c5c5b3429 Update arrogant_poet.txt 2021-04-02 10:23:33 +00:00
John
284d51918f Update tenured_inkcaster.txt 2021-04-02 10:22:31 +00:00
Anthony Calosa
06a1a49a73 Merge branch 'kevlahnota-master-patch-25321' into 'master'
Update tenured_inkcaster.txt

See merge request core-developers/forge!4346
2021-04-02 10:20:49 +00:00
Anthony Calosa
1c2c72ca7c Update tenured_inkcaster.txt 2021-04-02 10:20:37 +00:00
John
f2a28a34fe Update tome_shredder.txt 2021-04-02 09:33:14 +00:00
John
34e6eedf23 Add new file 2021-04-02 09:12:44 +00:00
John
c1042d15b5 Add new file 2021-04-02 09:04:14 +00:00
John
c40f258725 Add new file 2021-04-02 08:57:31 +00:00
John
8619c180de Add new file 2021-04-02 08:49:24 +00:00
John
1e9e5c5956 Add new file 2021-04-02 08:43:21 +00:00
tool4EvEr
095769b7cd Use StackDescription for AltCosts 2021-04-02 10:20:22 +02:00
Anthony Calosa
0740115724 Merge branch 'master' into 'master'
Remove Unused Import

See merge request core-developers/forge!4343
2021-04-02 07:52:46 +00:00
Anthony Calosa
862f2080ea Remove Unused Import 2021-04-02 15:51:46 +08:00
Anthony Calosa
2ed264b2b1 Merge branch 'new-cards-2' into 'master'
Add SpellCastOrCopy trigger

See merge request core-developers/forge!4338
2021-04-02 07:44:56 +00:00
Anthony Calosa
e9c2ea0e87 Merge branch 'master' into 'master'
Skip loading filtered card

See merge request core-developers/forge!4342
2021-04-02 06:34:41 +00:00
Anthony Calosa
79cea28630 Skip loading filtered card 2021-04-02 14:31:21 +08:00
Adam Pantel
ec6ed8cea5 Remove redundant checks 2021-04-02 00:31:49 -04:00
Michael Kamensky
eab7a03c77 Merge branch 'Williams-master-patch-02407' into 'master'
STX Vanishing Verse

See merge request core-developers/forge!4336
2021-04-02 04:08:48 +00:00
Michael Kamensky
970d82e48e Merge branch 'fuse' into 'master'
Fuse: add keyword to card text

See merge request core-developers/forge!4337
2021-04-02 04:08:27 +00:00
Michael Kamensky
a9ad5169f2 Merge branch 'squirrel' into 'master'
form_of_the_squirrel.txt fixes

See merge request core-developers/forge!4340
2021-04-02 04:08:12 +00:00
Michael Kamensky
27e0b3e62f Merge branch 'stx_1' into 'master'
STX - 1 Apr (no foolin)

See merge request core-developers/forge!4335
2021-04-02 04:07:30 +00:00
Michael Kamensky
3dfa2cd397 Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!4341
2021-04-02 04:06:11 +00:00
Michael Kamensky
782eca7b95 Merge branch 'new-cards' into 'master'
STX cards

See merge request core-developers/forge!4334
2021-04-02 04:06:00 +00:00
CCTV-1
17c6e46e8d update simplified chinese translation 2021-04-02 11:40:30 +08:00
Northmoc
684b3d2b2b form_of_the_squirrel.txt fixes 2021-04-01 22:25:05 -04:00
Northmoc
e0c4ee102c mastery description updates 2021-04-01 22:17:13 -04:00
Northmoc
0766a4b76d devastating_mastery.txt 2021-04-01 22:16:47 -04:00
Northmoc
e16bd40fd3 verdant_mastery.txt 2021-04-01 22:16:31 -04:00
Hans Mackowiak
479f72f160 AbilityUtils: add Valid to xCount to fix Mana Echoes 2021-04-02 00:53:46 +02:00
John
56bfb32ad4 Add new file 2021-04-01 21:13:20 +00:00
Adam Pantel
45dbb08993 STX cards 2021-04-01 17:09:43 -04:00
Bug Hunter
5030f6e901 Merge branch 'typo' into 'master'
Fix description

See merge request core-developers/forge!4339
2021-04-01 20:44:12 +00:00
tool4EvEr
e64003a22f Fix description 2021-04-01 22:43:54 +02:00
Adam Pantel
3abdeee850 Combine into one class 2021-04-01 16:23:33 -04:00
Adam Pantel
dfec2dc308 Add SpellCastOrCopy trigger 2021-04-01 15:36:51 -04:00
John
abede2f979 Add new file 2021-04-01 19:21:50 +00:00
John
695a0dac40 Add new file 2021-04-01 19:13:14 +00:00
John
266e2e13cf Add new file 2021-04-01 19:07:41 +00:00
Northmoc
79a81c86bb add AI hint 2021-04-01 14:47:29 -04:00
tool4EvEr
20f7bf5ebf Fuse: add keyword to card text 2021-04-01 20:46:31 +02:00
Northmoc
bc3cd33a30 fervent_mastery.txt 2021-04-01 14:41:20 -04:00
John
5eb3902854 Add new file 2021-04-01 18:27:25 +00:00
Northmoc
eb75911d50 baleful_mastery.txt 2021-04-01 12:26:48 -04:00
Michael Kamensky
81b352c888 Merge branch 'ai-multiple-choice' into 'master'
Add basic Multiple Choice AI, tweak Brain in a Jar AI. Also fix Multiple Choice being uncastable.

See merge request core-developers/forge!4330
2021-04-01 15:47:46 +00:00
Michael Kamensky
4b64dff6dd Merge branch 'fixdesc' into 'master'
Some fixes

See merge request core-developers/forge!4333
2021-04-01 15:47:29 +00:00
Michael Kamensky
ea2eb39975 Merge branch 'master' into 'master'
Strixhaven fun with these new cards

See merge request core-developers/forge!4331
2021-04-01 15:46:20 +00:00
Michael Kamensky
c3d64a7d2a Merge branch 'Williams-master-patch-81587' into 'master'
STX Some more cards

See merge request core-developers/forge!4332
2021-04-01 15:46:09 +00:00
Northmoc
4049a66c45 tamiyo_collector_of_tales.txt add ValidCards to +1 2021-04-01 11:33:45 -04:00
John
b06749aff9 Add new file 2021-04-01 13:29:39 +00:00
John
6fa418b43b Add new file 2021-04-01 12:30:26 +00:00
John
831786f55e Add new file 2021-04-01 12:23:41 +00:00
Michael Kamensky
952a9b69e1 Merge branch 'Williams-master-patch-62846' into 'master'
Some STX Cards 01/04

See merge request core-developers/forge!4329
2021-04-01 11:22:59 +00:00
Michael Kamensky
8d367a1167 - Add basic Multiple Choice AI
- Fix Brain in a Jar AI, move it to SpecialCardAi
2021-04-01 14:06:53 +03:00
John
6cb366a978 Add new file 2021-04-01 10:55:51 +00:00
John
aa99373d2d Add new file 2021-04-01 10:47:01 +00:00
John
a6633365ec Add new file 2021-04-01 10:46:11 +00:00
John
cac0f46d1c Add new file 2021-04-01 10:44:47 +00:00
Hythonia
3e3b14779e STX: Golden Ratio, Strixhaven Stadium and more 2021-04-01 11:26:34 +02:00
Michael Kamensky
225767acab Merge branch 'stx_31' into 'master'
STX - 31 Mar

See merge request core-developers/forge!4326
2021-04-01 08:06:10 +00:00
Michael Kamensky
7d1f31247e Merge branch 'stxed' into 'master'
New/updated edition files

See merge request core-developers/forge!4325
2021-04-01 08:04:08 +00:00
Michael Kamensky
5fe1de5f7f Merge branch 'multiple' into 'master'
Multiple Choice

See merge request core-developers/forge!4320
2021-04-01 08:02:39 +00:00
Michael Kamensky
0ac4f98ffa Merge branch 'practical' into 'master'
Practical Research and support

See merge request core-developers/forge!4323
2021-04-01 08:02:02 +00:00
Anthony Calosa
ebd3c72170 Merge branch 'master' into 'master'
fix check

See merge request core-developers/forge!4328
2021-04-01 07:54:41 +00:00
Anthony Calosa
839a91d144 fix check 2021-04-01 15:53:10 +08:00
Anthony Calosa
5c3df787bb Merge branch 'kevlahnota-master-patch-83054' into 'master'
Update blood_of_the_martyr.txt

See merge request core-developers/forge!4327
2021-04-01 06:30:26 +00:00
Anthony Calosa
daeec63b71 Update blood_of_the_martyr.txt 2021-04-01 06:30:09 +00:00
Northmoc
1aea0aa24c archway_commons.txt 2021-03-31 22:08:50 -04:00
Northmoc
7b53957e55 kasmina AI tweak 2021-03-31 22:04:08 -04:00
Northmoc
09c7af4cc4 manifestation_sage.txt 2021-03-31 22:03:49 -04:00
Northmoc
7ed9526828 leonin_lightscribe.txt 2021-03-31 21:53:09 -04:00
Northmoc
27c61f1596 hall_of_oracles.txt 2021-03-31 21:47:45 -04:00
Northmoc
ca8e4d19bf quandrix_campus.txt 2021-03-31 21:27:04 -04:00
Northmoc
938114f942 Strixhaven School of Mages.txt update 2021-03-31 17:10:58 -04:00
Northmoc
b7c61baafb Commander 2021.txt edition file 2021-03-31 17:10:08 -04:00
Northmoc
5850e271c3 moorland_haunt.txt clean up 2021-03-31 16:57:29 -04:00
Northmoc
7ab15776df ecological_appreciation.txt 2021-03-31 16:51:17 -04:00
Anthony Calosa
7f0b819993 Merge branch 'master' into 'master'
[Mobile] preload ItemPool and fix Planar Conquest new game

See merge request core-developers/forge!4324
2021-03-31 18:35:35 +00:00
Anthony Calosa
c0af1fa1eb [Mobile] preload ItemPool and fix Planar Conquest new game 2021-04-01 02:34:14 +08:00
Northmoc
316eb681f6 add AI hint 2021-03-31 10:09:28 -04:00
Northmoc
2a841e4ae2 AI multi UnlessType support 2021-03-31 09:59:54 -04:00
Northmoc
0033de0831 human multi UnlessType support 2021-03-31 09:58:54 -04:00
Northmoc
f70af84e16 practical_research.txt 2021-03-31 09:32:56 -04:00
Michael Kamensky
d9571f3c4d Merge branch 'Williams-master-patch-43202' into 'master'
STX some more cards

See merge request core-developers/forge!4317
2021-03-31 09:59:08 +00:00
Michael Kamensky
fda2f1ff15 Merge branch 'refactor_loadnonlegal' into 'master'
refactor enable non legal cards

See merge request core-developers/forge!4322
2021-03-31 09:58:54 +00:00
John
0d4824b7e0 Update sudden_breakthrough.txt 2021-03-31 07:45:18 +00:00
Hans Mackowiak
0cd376e039 CopySpellAbilityEffect: better description for optional 2021-03-31 09:26:45 +02:00
Hans Mackowiak
101c953020 Culmination of studies: better use of Remembered 2021-03-31 09:18:24 +02:00
John
46bf18cbfa Update resculpt.txt 2021-03-31 07:09:08 +00:00
Anthony Calosa
5348cd3f22 refactor enable non legal cards 2021-03-31 14:01:25 +08:00
Michael Kamensky
9591ed744b Merge branch 'stx_30' into 'master'
STX - 30 Mar

See merge request core-developers/forge!4316
2021-03-31 04:51:15 +00:00
Michael Kamensky
efed8bbd4b Merge branch 'new-cards' into 'master'
STX cards

See merge request core-developers/forge!4319
2021-03-31 04:50:11 +00:00
Michael Kamensky
7e0025d3fc Merge branch 'sym-master' into 'master'
Master Symmetrist

See merge request core-developers/forge!4308
2021-03-31 04:49:11 +00:00
Michael Kamensky
b016b56a30 Merge branch 'typo' into 'master'
typo

See merge request core-developers/forge!4321
2021-03-31 04:48:57 +00:00
Michael Kamensky
167d577f4d Merge branch 'gold' into 'master'
Fix Bound in Gold typos

See merge request core-developers/forge!4318
2021-03-31 04:48:50 +00:00
Northmoc
8888c97e07 multiple_choice.txt 2021-03-30 23:01:05 -04:00
Northmoc
b99e1f7b28 support for second SVar check 2021-03-30 22:59:59 -04:00
Adam Pantel
39b09c5b25 Symmetry Master 2021-03-30 19:31:41 -04:00
Adam Pantel
86842735bf STX cards 2021-03-30 18:37:33 -04:00
Northmoc
05cf67d3e1 prompt support for Wandering Archaic 2021-03-30 18:19:00 -04:00
Northmoc
9f10d1b923 wandering_archaic_explore_the_vastlands.txt 2021-03-30 18:19:00 -04:00
Northmoc
db66c6cde4 culmination_of_studies.txt 2021-03-30 18:19:00 -04:00
Northmoc
0eaa99a91a prismari_campus.txt 2021-03-30 18:18:59 -04:00
Northmoc
11de057064 extus_oriq_overlord_awaken_the_blood_avatar.txt and token 2021-03-30 18:18:59 -04:00
Northmoc
d206ed5c0b tanazir_quandrix.txt 2021-03-30 18:18:58 -04:00
Northmoc
6dd0275e5a galazeth_prismari.txt 2021-03-30 18:18:58 -04:00
Hans Mackowiak
cefaa78832 Semester's End: needs to be ReplacementEffect like Spark Double 2021-03-30 22:52:10 +02:00
John
7253c56086 Add new file 2021-03-30 20:27:39 +00:00
tool4EvEr
38dc4274ff Fix Bound in Gold typos 2021-03-30 21:59:30 +02:00
John
833a5bd974 Update sudden_breakthrough.txt 2021-03-30 19:43:18 +00:00
John
2443772e78 Add new file 2021-03-30 19:41:50 +00:00
John
243a24c7f2 Add new file 2021-03-30 19:34:22 +00:00
Northmoc
1166297dda fetch counter 2021-03-30 15:29:33 -04:00
Anthony Calosa
58bfc4b36c Merge branch 'tweaks' into 'master'
Tweaks

See merge request core-developers/forge!4315
2021-03-30 17:53:20 +00:00
Anthony Calosa
1b07c8838f Merge branch 'kevlahnota-master-patch-57147' into 'master'
fix NPE

See merge request core-developers/forge!4314
2021-03-30 17:28:31 +00:00
Anthony Calosa
6592694778 fix NPE 2021-03-30 17:28:08 +00:00
Northmoc
08d5735af9 one SubAbility per line 2021-03-30 13:22:07 -04:00
Northmoc
4cd69a52f6 better prompt 2021-03-30 13:21:13 -04:00
Michael Kamensky
542bab816f Merge branch 'new-cards' into 'master'
STX cards

See merge request core-developers/forge!4306
2021-03-30 17:19:24 +00:00
Michael Kamensky
357be5296f Merge branch 'Williams-master-patch-83839' into 'master'
STX some cards 30/03

See merge request core-developers/forge!4310
2021-03-30 17:18:19 +00:00
Hans Mackowiak
57816a4f3a fixup DestroyAi use getMaxXValue 2021-03-30 19:08:15 +02:00
John
7486eb640f Add new file 2021-03-30 16:51:31 +00:00
John
e946e23360 Update illustrious_historian.txt 2021-03-30 16:47:00 +00:00
Adam Pantel
e233eed8eb STX cards 2021-03-30 12:44:14 -04:00
Michael Kamensky
80ac8906b3 Merge branch 'stx_29' into 'master'
STX - 29 Mar

See merge request core-developers/forge!4302
2021-03-30 16:24:46 +00:00
Michael Kamensky
ee9cb656fc Merge branch 'master' into 'master'
STX: A couple more cards

See merge request core-developers/forge!4305
2021-03-30 16:23:44 +00:00
Michael Kamensky
cfe9f8e6e6 Merge branch 'master' into 'master'
Fix Heliod's Intervention AI

See merge request core-developers/forge!4313
2021-03-30 16:23:24 +00:00
Michael Kamensky
72f768cca4 - Fix Heliod's Intervention AI 2021-03-30 19:22:46 +03:00
Northmoc
9d0206c432 lorehold_excavation.txt AI fix 2021-03-30 12:19:35 -04:00
Anthony Calosa
53bc91d399 Merge branch 'master' into 'master'
refactor CardDB

See merge request core-developers/forge!4312
2021-03-30 15:44:06 +00:00
Anthony Calosa
ca63763d04 refactor CardDB
use filterentries instead of declaring maps
2021-03-30 23:42:33 +08:00
John
ac7589a3ff Add new file 2021-03-30 15:03:48 +00:00
John
d752442aa3 Add new file 2021-03-30 14:48:53 +00:00
Anthony Calosa
930c27a890 Merge branch 'Williams-master-patch-81518' into 'master'
Update returned_pastcaller.txt

See merge request core-developers/forge!4311
2021-03-30 11:24:28 +00:00
John
2caec7be0e Update returned_pastcaller.txt 2021-03-30 10:44:50 +00:00
John
a7cfe98ca3 Update illustrious_historian.txt 2021-03-30 10:03:10 +00:00
John
bad5f6f431 Update illustrious_historian.txt 2021-03-30 10:02:51 +00:00
John
d0116c852c Add new file 2021-03-30 10:01:56 +00:00
Hythonia
3f3ce39b30 STX: Yet more cards 2021-03-30 10:02:08 +02:00
Bug Hunter
8a5692e9df Merge branch 'TRT-master-patch-00357' into 'master'
ActivateAbilityAi: small revert/align for multiplayer

See merge request core-developers/forge!4309
2021-03-30 07:58:56 +00:00
Bug Hunter
5cf96b1154 Update forge-ai/src/main/java/forge/ai/ability/ActivateAbilityAi.java 2021-03-30 07:56:46 +00:00
Michael Kamensky
2d392de9ef Merge branch 'spellAbilityAdditionalAbilities' into 'master'
SpellAbility: use SpellAbility for additionalAbilities so Trigger can have cost

See merge request core-developers/forge!4303
2021-03-30 04:47:45 +00:00
Hans Mackowiak
f53aff1882 SpellAbility: use SpellAbility for additionalAbilities so Trigger can have cost 2021-03-30 04:47:44 +00:00
Michael Kamensky
0cf6cbf3bc Merge branch 'proliferate' into 'master'
Fix AI killing itself with poison + proliferate

See merge request core-developers/forge!4307
2021-03-30 04:12:46 +00:00
Michael Kamensky
28b428a6f1 Merge branch 'Williams-master-patch-92703' into 'master'
STX more cards

See merge request core-developers/forge!4300
2021-03-30 04:12:10 +00:00
Northmoc
d311ced441 campus_guide.txt 2021-03-29 19:10:17 -04:00
tool4EvEr
3ccce18f23 Fix AI killing itself with poison + proliferate 2021-03-29 23:27:38 +02:00
Northmoc
0ecd719799 curate.txt 2021-03-29 17:12:12 -04:00
Northmoc
5438e20b01 lorehold_campus.txt 2021-03-29 15:10:42 -04:00
Northmoc
b5f72ecc5c hofri_ghostforge.txt 2021-03-29 14:06:35 -04:00
Hythonia
dd20c8caf0 STX: Some more cards 2021-03-29 19:49:27 +02:00
Northmoc
c3b96e4f68 illuminate_history.txt 2021-03-29 13:09:51 -04:00
Northmoc
b3132aa757 reconstruct_history.txt 2021-03-29 13:09:50 -04:00
Northmoc
7685e3233f blade_historian.txt 2021-03-29 13:09:50 -04:00
Northmoc
ee3e66580d lorehold_excavation.txt 2021-03-29 13:09:50 -04:00
Northmoc
26cd2acbcd velomachus_lorehold.txt 2021-03-29 13:09:49 -04:00
Northmoc
0c3b099265 radiant_scrollwielder.txt 2021-03-29 13:09:49 -04:00
Bug Hunter
6971d320ae Merge branch 'fixRemembered' into 'master'
Fix RememberDiscarded

See merge request core-developers/forge!4304
2021-03-29 17:03:48 +00:00
John
2dd6305e54 Add new file 2021-03-29 17:02:42 +00:00
tool4EvEr
baf5479d0b Fix RememberDiscarded 2021-03-29 18:52:13 +02:00
Hythonia
c57be04143 STX: Some cards 2021-03-29 18:40:17 +02:00
John
f1030c2f10 Add new file 2021-03-29 16:31:09 +00:00
John
1f1639e661 Update quintorius_field_historian.txt 2021-03-29 16:10:06 +00:00
John
04d3445834 Add new file 2021-03-29 16:05:58 +00:00
John
1559a14e39 Add new file 2021-03-29 15:58:22 +00:00
John
fbbc6aa005 Update quintorius_field_historian.txt 2021-03-29 15:31:58 +00:00
John
9c573b9bfd Update returned_pastcaller.txt 2021-03-29 15:01:17 +00:00
John
96bbe2c7dc Update returned_pastcaller.txt 2021-03-29 14:59:55 +00:00
John
ec9f6b82d9 Update quintorius_field_historian.txt 2021-03-29 14:58:26 +00:00
John
2c120c3c45 Add new file 2021-03-29 14:58:03 +00:00
John
b911f5c45b Update returned_pastcaller.txt 2021-03-29 14:56:38 +00:00
John
6366a22ff3 Update returned_pastcaller.txt 2021-03-29 14:55:17 +00:00
John
5fc819d8f5 Add new file 2021-03-29 14:43:15 +00:00
John
df9d1ddcbf Add new file 2021-03-29 14:00:44 +00:00
Anthony Calosa
eed7b282d3 Merge branch 'master' into 'master'
Refactor carddb for Deck Editor/Workshop Catalog

Closes #1436

See merge request core-developers/forge!4298
2021-03-29 13:44:55 +00:00
Anthony Calosa
fc6237cd29 update advance filter
support alternate parts limited to card names, keywords, rules text, types and subtypes
2021-03-29 21:26:14 +08:00
John
c473e634e0 Add new file 2021-03-29 12:20:38 +00:00
John
28866fc1dc Add new file 2021-03-29 12:17:00 +00:00
John
8028e8ca12 Add new file 2021-03-29 12:13:42 +00:00
Anthony Calosa
28d1cc2055 Merge remote-tracking branch 'core/master' 2021-03-29 18:30:55 +08:00
Anthony Calosa
cffd99b68d update advance filter 2021-03-29 18:30:09 +08:00
Michael Kamensky
ac92a92090 Merge branch 'targetselection' into 'master'
TargetSelection: Fix stack peeking detection

See merge request core-developers/forge!4295
2021-03-29 08:00:21 +00:00
Bug Hunter
28dbb309cf TargetSelection: Fix stack peeking detection 2021-03-29 08:00:21 +00:00
Michael Kamensky
7b585aaa05 Merge branch 'descriptions' into 'master'
Fix alternate state descriptions when viewed from original

See merge request core-developers/forge!4277
2021-03-29 07:59:43 +00:00
Bug Hunter
82f0c85bd0 Fix alternate state descriptions when viewed from original 2021-03-29 07:59:43 +00:00
Michael Kamensky
1c0a7d8475 Merge branch 'Williams-master-patch-39368' into 'master'
Update adrix_and_nev_twincasters.txt

See merge request core-developers/forge!4296
2021-03-29 07:58:48 +00:00
Michael Kamensky
5f63b5f907 Merge branch 'card-fixes' into 'master'
Inzerva doesn't target

See merge request core-developers/forge!4297
2021-03-29 07:58:38 +00:00
Anthony Calosa
7459cd65d9 unused variable 2021-03-29 15:23:57 +08:00
Anthony Calosa
0b61ddaff7 fix advance filter for adventure and flip 2021-03-29 15:23:03 +08:00
Anthony Calosa
d554a7ee97 add getUniqueCardsNoAlt for mobile 2021-03-29 13:35:20 +08:00
Anthony Calosa
3d1e130b1e Merge remote-tracking branch 'core/master' 2021-03-29 12:47:11 +08:00
Anthony Calosa
a63e008f84 Merge branch 'kevlahnota-master-patch-97416' into 'master'
fix cards

See merge request core-developers/forge!4299
2021-03-29 04:46:29 +00:00
Anthony Calosa
d76ae27611 fix cards 2021-03-29 04:46:29 +00:00
Anthony Calosa
bf00d79e23 refactor carddb for Deck Editor/Workshop Catalog 2021-03-29 11:19:26 +08:00
Adam Pantel
c08775f3e3 Inzerva doesn't target 2021-03-28 22:03:12 -04:00
John
d94ceaf492 Update adrix_and_nev_twincasters.txt 2021-03-28 21:57:32 +00:00
Michael Kamensky
9802cb62d9 Merge branch 'update_word_of_command' into 'master'
Update Word of Command mana payment check conditions

See merge request core-developers/forge!4291
2021-03-28 12:26:30 +00:00
Anthony Calosa
2c4f90b31c Merge branch 'master' into 'master'
Restrict Deck Editor cards

See merge request core-developers/forge!4294
2021-03-28 09:59:21 +00:00
Anthony Calosa
163d95800b Restrict Deck Editor cards
- fix duplicate cards
2021-03-28 17:55:23 +08:00
Michael Kamensky
0662d44a86 Merge branch 'Williams-master-patch-67517' into 'master'
Update Strixhaven Mystical Archive.txt

See merge request core-developers/forge!4293
2021-03-28 09:36:11 +00:00
Michael Kamensky
c3792191a2 Merge branch 'Williams-master-patch-03072' into 'master'
STX Lesson Cards

See merge request core-developers/forge!4289
2021-03-28 09:36:01 +00:00
Michael Kamensky
97ff18c3e2 Merge branch 'uvilda' into 'master'
Uvilda

See merge request core-developers/forge!4288
2021-03-28 09:35:44 +00:00
Michael Kamensky
d069252f76 Merge branch 'new-cards' into 'master'
Zaffai

See merge request core-developers/forge!4290
2021-03-28 09:35:39 +00:00
John
54fda151be Update Strixhaven Mystical Archive.txt 2021-03-28 08:43:02 +00:00
John
04c4cf4426 Update osgir_the_Reconstructor.txt 2021-03-28 08:04:23 +00:00
John
7c81fe3339 Add new file 2021-03-28 08:03:38 +00:00
John
330862e6c0 Update adrix_and_nev_twincasters.txt 2021-03-28 07:41:09 +00:00
John
ce309a384e Update environmental_sciences.txt 2021-03-28 07:38:14 +00:00
Adam Pantel
6bc01c3472 Uvilda 2021-03-28 03:14:37 -04:00
Adam Pantel
9ecf3e11bf Zaffai 2021-03-28 03:12:59 -04:00
Bug Hunter
7da306eb65 Merge branch 'fixDraw' into 'master'
Fix NPE when targeting different players hidden zone

See merge request core-developers/forge!4292
2021-03-28 07:08:44 +00:00
tool4EvEr
3d9d6d45fe Fix NPE when targeting different players hidden zone 2021-03-28 09:05:31 +02:00
Lyu Zong-Hong
7fc85a47be Update Word of Command mana payment check conditions 2021-03-28 15:09:01 +09:00
John
6accf7fd6c Update environmental_sciences.txt 2021-03-27 22:42:37 +00:00
John
f619155667 Update introduction_to_anihilation.txt 2021-03-27 19:34:23 +00:00
John
89d50af521 Update expanded_anatomy.txt 2021-03-27 19:32:18 +00:00
John
fe3a111d6f Update adrix_and_nev_twincasters.txt 2021-03-27 19:31:43 +00:00
John
75b6b6316b Add new file 2021-03-27 19:30:57 +00:00
John
f64822a18b Add new file 2021-03-27 19:29:45 +00:00
John
e566fb2d57 Add new file 2021-03-27 19:29:14 +00:00
John
9f5931509c Add new file 2021-03-27 19:28:39 +00:00
John
deb7ee8c3a Add new file 2021-03-27 19:27:13 +00:00
Hans Mackowiak
6a56e29474 Breena with better Choices 2021-03-27 17:19:39 +01:00
Michael Kamensky
c79ccd7e23 Merge branch 'stx_27' into 'master'
STX - 27 Mar

See merge request core-developers/forge!4287
2021-03-27 15:54:51 +00:00
Michael Kamensky
36e395e3a3 Merge branch 'master' into 'master'
STX cards + Abundant Harvest

See merge request core-developers/forge!4285
2021-03-27 15:54:25 +00:00
Northmoc
c0b106c7ef valentin_dean_of_the_vein_lisette_dean_of_the_root.txt 2021-03-27 11:40:28 -04:00
Hythonia
23c8c5da19 Will fix 2021-03-27 15:37:30 +01:00
Hythonia
883f1a7cf8 DeckHas/DeckHints 2021-03-27 15:35:37 +01:00
Michael Kamensky
b2ff43bd9b Merge branch 'twosat-master-patch-55523' into 'master'
Update de-DE.properties

See merge request core-developers/forge!4284
2021-03-27 14:06:30 +00:00
Michael Kamensky
471bb1cacd Merge branch 'fix_mutate_mdfc' into 'master'
Fix crash when mutate under an MDFC backside creature.

See merge request core-developers/forge!4286
2021-03-27 14:05:08 +00:00
Northmoc
de30329a58 prismari_apprentice.txt 2021-03-27 09:23:51 -04:00
Lyu Zong-Hong
c990c8af56 Fix crash when mutate under an MDFC backside creature. 2021-03-27 22:00:20 +09:00
Hythonia
2dbd099c68 STX: Inkling type 2021-03-27 13:55:54 +01:00
Hythonia
25da363d56 STX: Rowan // Will 2021-03-27 13:50:10 +01:00
Hythonia
a957566fc9 STX: Lorehold Apprentice 2021-03-27 13:28:45 +01:00
Michael Kamensky
97db42427e Merge branch 'shaileEnterUnder' into 'master'
Card: add turnInController to keep track under which control it entered

See merge request core-developers/forge!4282
2021-03-27 12:14:09 +00:00
Hythonia
34fabaab5f STA: Abundant Harvest 2021-03-27 12:33:41 +01:00
Andreas Bendel
11893e244e Update de-DE.properties
some Choose-strings
2021-03-27 11:29:17 +00:00
Hythonia
24f1285a43 Breena and PlayerProperty.java update 2021-03-27 12:02:13 +01:00
Anthony Calosa
2d2e0fd6c0 Merge branch 'kevlahnota-master-patch-21990' into 'master'
set correct imagekey for rollback

See merge request core-developers/forge!4283
2021-03-27 10:54:51 +00:00
Anthony Calosa
5ea2aca8a8 set correct imagekey for rollback 2021-03-27 10:51:53 +00:00
Hans Mackowiak
088b4fa07e Card: add turnInController to keep track under which control it entered 2021-03-27 11:35:52 +01:00
Hans Mackowiak
7dcfbc409c STX: fix Execute$ Execute$ 2021-03-27 10:13:06 +01:00
Michael Kamensky
44bb2ff0f5 Merge branch 'drawing' into 'master'
Move all hidden zone changing after choosing

See merge request core-developers/forge!4256
2021-03-27 04:37:27 +00:00
Bug Hunter
4effb02219 Move all hidden zone changing after choosing 2021-03-27 04:37:27 +00:00
Michael Kamensky
5d7ad6bb81 Merge branch 'master' into 'master'
STX: One Ward, two card

See merge request core-developers/forge!4273
2021-03-27 04:37:13 +00:00
Michael Kamensky
2bef5d7653 Merge branch 'c21_26' into 'master'
some C21 scripts

See merge request core-developers/forge!4274
2021-03-27 04:36:54 +00:00
Michael Kamensky
6b76bec480 Merge branch 'new-cards' into 'master'
Some STX cards

See merge request core-developers/forge!4276
2021-03-27 04:36:31 +00:00
Michael Kamensky
76ab53078e Merge branch 'stx_26' into 'master'
STX - 26 Mar (+Lesson type)

See merge request core-developers/forge!4278
2021-03-27 04:35:36 +00:00
Michael Kamensky
ddf8df9c71 Merge branch 'fixStartDraw' into 'master'
Don't count draws before game started

Closes #1781

See merge request core-developers/forge!4275
2021-03-27 04:34:09 +00:00
Michael Kamensky
b312f28ab6 Merge branch 'add_word_of_command' into 'master'
Add Word of Command

See merge request core-developers/forge!4281
2021-03-27 04:33:42 +00:00
Michael Kamensky
d9e536b789 Merge branch 'shadrix' into 'master'
STX: Shadrix, token, and CharmEffect support

See merge request core-developers/forge!4271
2021-03-27 04:33:01 +00:00
Lyu Zong-Hong
2e768cb77b Add Word of Command 2021-03-27 12:25:20 +09:00
Northmoc
d611dbd386 quandrix_apprentice.txt 2021-03-26 20:30:43 -04:00
Northmoc
8976e238a5 silverquill_apprentice.txt 2021-03-26 20:27:22 -04:00
Northmoc
eb13bb8b6d witherbloom_apprentice.txt 2021-03-26 20:26:05 -04:00
Northmoc
d72eba1602 archmage_emeritus.txt 2021-03-26 20:21:45 -04:00
Northmoc
cd4685c093 eager_first_year.txt 2021-03-26 20:08:19 -04:00
Northmoc
bc3aa56ed9 remove unneeded NoResolvingCheck 2021-03-26 19:50:13 -04:00
Northmoc
903ead9e91 star_pupil.txt 2021-03-26 19:47:33 -04:00
Northmoc
bacbd8baf3 confront_the_past.txt 2021-03-26 19:36:07 -04:00
Bug Hunter
407c742124 Merge branch 'fixNPE' into 'master'
AI: Fix NPE

See merge request core-developers/forge!4280
2021-03-26 21:34:09 +00:00
tool4EvEr
34a91b7e3d Fix NPE 2021-03-26 22:33:23 +01:00
Northmoc
65f4724f87 add Lesson type 2021-03-26 16:56:21 -04:00
Northmoc
afafe46221 pest_summoning.txt and token 2021-03-26 16:55:56 -04:00
Northmoc
ff220113af storm_kiln_artist.txt (script by ?) 2021-03-26 15:28:00 -04:00
Adam Pantel
81c2c482cd Treasure Keeper, Kianne, Plargg 2021-03-26 15:25:10 -04:00
tool4EvEr
afb06baa56 Don't count draws before game started 2021-03-26 20:20:24 +01:00
Northmoc
d25ff61f22 dragonsguard_elite.txt (script by ?) 2021-03-26 15:12:08 -04:00
Northmoc
067f0b3a66 bandaid for CharmEffect.java description builder 2021-03-26 14:44:32 -04:00
Northmoc
0f71efd1e9 willowdusk_essence_seer.txt 2021-03-26 14:39:01 -04:00
Hythonia
351f9c1976 STX: Ward's TriggerDescription 2021-03-26 18:14:27 +01:00
Northmoc
a11eff4d9a add Optional param and prompt to CharmEffect.java 2021-03-26 12:44:11 -04:00
Northmoc
fe6a5a700a add optional Charm prompt 2021-03-26 12:43:34 -04:00
Northmoc
87a994b8ce shadrix_silverquill.txt v2 2021-03-26 12:42:52 -04:00
Hythonia
3f06553604 Merge remote-tracking branch 'origin/master' 2021-03-26 16:34:34 +01:00
Northmoc
e9f765a216 wb_2_1_inkling_flying.txt 2021-03-26 11:29:28 -04:00
Northmoc
532e65f3a0 shadrix_silverquill.txt 2021-03-26 11:29:08 -04:00
Hythonia
b038044347 STX: One Ward, two card 2021-03-26 15:01:59 +01:00
Michael Kamensky
0664d11f55 Merge branch 'master' into 'master'
Basic logic for Professor Onyx

See merge request core-developers/forge!4270
2021-03-26 11:48:04 +00:00
Michael Kamensky
6d00a26530 - Basic logic for Professor Onyx. 2021-03-26 14:38:10 +03:00
Michael Kamensky
a4d28b13ef Merge branch 'AIopponent' into 'master'
AI Multiplayer improvements

See merge request core-developers/forge!4239
2021-03-26 10:58:22 +00:00
Bug Hunter
fd01dc1bb1 AI Multiplayer improvements 2021-03-26 10:58:22 +00:00
Michael Kamensky
fa72d3d0de Merge branch 'master' into 'master'
update simplified chinese translation

See merge request core-developers/forge!4269
2021-03-26 10:57:20 +00:00
Michael Kamensky
f935f57d23 Merge branch 'add_camouflage' into 'master'
Add Camouflage

See merge request core-developers/forge!4268
2021-03-26 10:57:03 +00:00
Michael Kamensky
fbf1e855ad Merge branch 'snarls' into 'master'
STX rare land cycle

See merge request core-developers/forge!4263
2021-03-26 10:56:25 +00:00
Michael Kamensky
50a6037a6d Merge branch 'onyx' into 'master'
professor_onyx.txt

See merge request core-developers/forge!4262
2021-03-26 10:56:10 +00:00
CCTV-1
d15d27defb update simplified chinese translation 2021-03-26 18:40:04 +08:00
Hythonia
7952bdcb48 Revert "ManaEffect rework"
This reverts commit 9db11ce6
2021-03-26 11:37:57 +01:00
Hythonia
dfdf3160d3 Merge remote-tracking branch 'origin/master' 2021-03-26 11:20:36 +01:00
Lyu Zong-Hong
ce4f12179c Add Camouflage 2021-03-26 13:05:04 +09:00
Leandro Doctors
491da787b4 Merge branch 'update-building-docs' into 'master'
doc: update Android building instructions

See merge request core-developers/forge!4265
2021-03-26 02:01:43 +00:00
Leandro Doctors
6dbc7e2704 doc: update Android building instructions 2021-03-25 22:58:15 -03:00
Leandro Doctors
2a39c917ee Merge branch 'mention-hardcoded-value' into 'master'
add FIXME for hardcoded value

See merge request core-developers/forge!4264
2021-03-26 01:51:37 +00:00
Leandro Doctors
3733ba45e2 add FIXME for hardcoded value 2021-03-25 22:50:13 -03:00
Northmoc
2a410b9099 snarls 2021-03-25 20:14:22 -04:00
Northmoc
3ccc7c41e7 professor_onyx.txt v2 2021-03-25 18:13:33 -04:00
Northmoc
d03bc797fc DiscardEffect.java add "RememberDiscardingPlayers" param 2021-03-25 18:13:10 -04:00
Leandro Doctors
5c4f8eb526 Merge branch 'clean-up-pom' into 'master'
Delete obsolete 'developers' tags (pom.xml)

See merge request core-developers/forge!4219
2021-03-25 17:41:42 +00:00
Northmoc
973464f1c4 professor_onyx.txt 2021-03-25 10:56:40 -04:00
Hans Mackowiak
e090ab2825 Merge branch '1779-lingering-hostage-taker-untilhostleavesplay-issue' into 'master'
Resolve "Lingering Hostage Taker (UntilHostLeavesPlay) issue"

Closes #1779

See merge request core-developers/forge!4261
2021-03-25 13:51:07 +00:00
Hans Mackowiak
0413e5ded8 Fix ExiledWith being cleared when successfully added to MagicStack 2021-03-25 14:50:25 +01:00
Michael Kamensky
51c24f6fdf Merge branch 'fix_invasion_plans' into 'master'
Fix Invasion Plans

See merge request core-developers/forge!4260
2021-03-25 13:40:21 +00:00
Lyu Zong-Hong
d1de248894 Fix Invasion Plans 2021-03-25 21:19:28 +09:00
Hans Mackowiak
fdf3e618c2 Merge branch '1778-untilhostleavesplay-missing-a-check-on-hostage-taker' into 'master'
Resolve "UntilHostLeavesPlay missing a check on Hostage Taker"

Closes #1778

See merge request core-developers/forge!4259
2021-03-25 07:52:04 +00:00
Hans Mackowiak
5b0a228f4f SpellAbilityEffect: fix untilHostLeavesPlayCommand to check the until CardCollection 2021-03-25 08:51:35 +01:00
Hythonia
9db11ce690 ManaEffect rework 2021-03-24 20:14:41 +01:00
Leandro Doctors
f9bdf6e181 Delete obsolete 'developers' tags
That information is obsolete. The up to date version can always be obtained form version control.
2021-03-19 11:38:30 -03:00
5408 changed files with 62158 additions and 31833 deletions

3
.gitignore vendored
View File

@@ -17,6 +17,7 @@
.vscode/settings.json
.vscode/launch.json
.factorypath
# Ignore NetBeans config files
@@ -32,6 +33,8 @@ bin
gen
*.log
# Ignore macOS Spotlight rubbish
.DS_Store
# TODO: specify what these ignores are for (releasing?)

View File

@@ -1,15 +1,15 @@
# Forge
Gitlab repo is found [here](https://git.cardforge.org/core-developers/forge).
[Official GitLab repo](https://git.cardforge.org/core-developers/forge).
Dev instructions here: [Getting Started](https://www.slightlymagic.net/wiki/Forge:How_to_Get_Started_Developing_Forge) (Somewhat outdated)
Dev instructions here: [Getting Started](https://git.cardforge.org/core-developers/forge/-/wikis/(SM-autoconverted)-how-to-get-started-developing-forge) (Somewhat outdated)
Discord channel [here](https://discordapp.com/channels/267367946135928833/267742313390931968)
# Requirements / Tools
## Requirements / Tools
- Java IDE such as IntelliJ or Eclipse
- Java JDK 8 or later
- you favourite Java IDE (IntelliJ, Eclipse, VSCodium, Emacs, Vi...)
- Java JDK 8 or later (some IDEs such as Eclipse require JDK11+, whereas the Android build currently only works with JDK8)
- Git
- Git client (optional)
- Maven
@@ -18,7 +18,7 @@ Discord channel [here](https://discordapp.com/channels/267367946135928833/267742
- Android SDK (optional: for Android releases)
- RoboVM (optional: for iOS releases) (TBD: Current status of support by libgdx)
# Project Quick Setup
## Project Quick Setup
- Log in to gitlab with your user account and fork the project.
@@ -26,11 +26,11 @@ Discord channel [here](https://discordapp.com/channels/267367946135928833/267742
- Go to the project location on your machine. Run Maven to download all dependencies and build a snapshot. Example for Windows & Linux: `mvn -U -B clean -P windows-linux install`
# Eclipse
## Eclipse
Eclipse includes Maven integration so a separate install is not necessary. For other IDEs, your mileage may vary.
## Project Setup
### Project Setup
- Follow the instructions for cloning from Gitlab. You'll need a Gitlab account setup and an SSH key defined.
@@ -55,9 +55,9 @@ Eclipse includes Maven integration so a separate install is not necessary. For
- Once everything builds, all errors should disappear. You can now advance to Project launch.
## Project Launch
### Project Launch
### Desktop
#### Desktop
This is the standard configuration used for releasing to Windows / Linux / MacOS.
@@ -65,7 +65,7 @@ This is the standard configuration used for releasing to Windows / Linux / MacOS
- The familiar Forge splash screen, etc. should appear. Enjoy!
### Mobile (Desktop dev)
#### Mobile (Desktop dev)
This is the configuration used for doing mobile development using the Windows / Linux / MacOS front-end. Knowledge of libgdx is helpful here.
@@ -73,24 +73,24 @@ This is the configuration used for doing mobile development using the Windows /
- A view similar to a mobile phone should appear. Enjoy!
## Eclipse / Android SDK Integration
### Eclipse / Android SDK Integration
Google no longer supports Android SDK releases for Eclipse. That said, it is still possible to build and debug Android platforms.
### Android SDK
#### Android SDK
Reference SO for obtaining a specific release: https://stackoverflow.com/questions/27043522/where-can-i-download-an-older-version-of-the-android-sdk
#### Windows
##### Windows
Download the following archived version of the Android SDK: http://dl-ssl.google.com/android/repository/tools_r25.2.3-windows.zip. Install it somewhere on your machine. This is referenced
in the following instructions as your 'Android SDK Install' path.
#### Linux / Mac OSX
##### Linux / Mac OSX
TBD
### Android Plugin for Eclipse
#### Android Plugin for Eclipse
Google's last plugin release does not work completely with target's running Android 7.0 or later. Download the ADT-24.2.0-20160729.zip plugin
from: https://github.com/khaledev/ADT/releases
@@ -98,25 +98,24 @@ from: https://github.com/khaledev/ADT/releases
In Eclipse go to: Help > Install New Software... > Add > Name: ADT Update, Click on the "Archive:" button and navigate to the downloaded ADT-24.2.0-20160729.zip file > Add. Install all "Developer Tools". Eclipse
should restart and prompt you to run the SDK Manager. Launch it and continue to the next steps below.
### Android Platform
#### Android Platform
In Eclipse, if the SDK Manager is not already running, go to Window > Android SDK Manager. Install the following options / versions:
- Android SDK Build-tools 26.0.1
- Android 7.1.1 (API 25) SDK Platform
- Google USB Driver 11
- Android 8.0.0 (API 26) SDK Platform
- Google USB Driver (in case your phone is not detected by ADB)
Note that this will populate additional tools in the Android SDK install path extracted above.
### Proguard update
#### Proguard update
The Proguard included with the Android SDK Build-tools is outdated and does not work with Java 1.8. Download Proguard 6.0.3 from https://sourceforge.net/projects/proguard/files/proguard/6.0/.
The Proguard included with the Android SDK Build-tools is outdated and does not work with Java 1.8. Download Proguard 6.0.3 or later (last tested with 7.0.1) from https://github.com/Guardsquare/proguard
- Go to the Android SDK install path. Rename the tools/proguard/ path to tools/proguard-4.7/.
- Go to the Android SDK install path. Rename the tools/proguard/ path to tools/proguard4.7/.
- Extract your Proguard version to the Android SDK install path under tools/. You will need to either rename the dir proguard-<your-version> to proguard/ or, if your filesystem supports it, use a symbolic link (the later is highly recommended), such as `ln -s proguard proguard-<your-version>`.
- Extract Proguard 6.0.3 to the Android SDK install path under tools/. You will need to rename the dir proguard6.0.3/ to proguard/.
### Android Build
#### Android Build
The Eclipse plug-ins do NOT support building things for Android. They do however allow you to use the debugger so you can still set breakpoints and trace
things out. The steps below show how to generate a debug Android build.
@@ -135,7 +134,7 @@ things out. The steps below show how to generate a debug Android build.
Assuming you got this far, you should have an Android forge-android-[version].apk in the forge-gui-android/target path.
### Android Deploy
#### Android Deploy
You'll need to have the Android SDK install path platform-tools/ path in your command search path to easily deploy builds.
@@ -149,14 +148,14 @@ You'll need to have the Android SDK install path platform-tools/ path in your co
- Install the new apk: `adb install forge-android-[version].apk`
### Android Debugging
#### Android Debugging
Assuming the apk is installed, launch it from the device.
In Eclipse, launch the DDMS. Window > Perspective > Open Perspective > Other... > DDMS. You should see the forge app in the list. Highlight the app, click on the green debug button and a
green debug button should appear next to the app's name. You can now set breakpoints and step through the source code.
## Windows / Linux SNAPSHOT build
### Windows / Linux SNAPSHOT build
SNAPSHOT builds can be built via the Maven integration in Eclipse.
@@ -167,19 +166,19 @@ SNAPSHOT builds can be built via the Maven integration in Eclipse.
The resulting snapshot will be found at: forge-gui-desktop/target/forge-gui-desktop-[version]-SNAPSHOT
# IntelliJ
## IntelliJ
Quick start guide for [setting up the Forge project within IntelliJ](https://git.cardforge.org/core-developers/forge/wikis/intellij-setup).
Quick start guide for [setting up the Forge project within IntelliJ](https://git.cardforge.org/core-developers/forge/-/wikis/Development/intellij-setup).
# Card Scripting
## Card Scripting
Visit [this page](https://www.slightlymagic.net/wiki/Forge_API) for information on scripting.
Visit [this page](https://git.cardforge.org/core-developers/forge/-/wikis/Card-scripting-API/Card-scripting-API) for information on scripting.
Card scripting resources are found in the forge-gui/res/ path.
# General Notes
## General Notes
## Project Hierarchy
### Project Hierarchy
Forge is divided into 4 primary projects with additional projects that target specific platform releases. The primary projects are:
@@ -196,35 +195,34 @@ The platform-specific projects are:
- forge-gui-mobile
- forge-gui-mobile-dev
### forge-ai
#### forge-ai
### forge-core
#### forge-core
### forge-game
#### forge-game
### forge-gui
#### forge-gui
The forge-gui project includes the scripting resource definitions in the res/ path.
### forge-gui-android
#### forge-gui-android
Libgdx-based backend targeting Android. Requires Android SDK and relies on forge-gui-mobile for GUI logic.
### forge-gui-desktop
#### forge-gui-desktop
Java Swing based GUI targeting desktop machines.
Screen layout and game logic revolving around the GUI is found here. For example, the overlay arrows (when enabled) that indicate attackers and blockers, or the targets of the stack are defined and drawn by this.
### forge-gui-ios
#### forge-gui-ios
Libgdx-based backend targeting iOS. Relies on forge-gui-mobile for GUI logic.
### forge-gui-mobile
#### forge-gui-mobile
Mobile GUI game logic utilizing [libgdx](https://libgdx.badlogicgames.com/) library. Screen layout and game logic revolving around the GUI for the mobile platforms is found here.
### forge-gui-mobile-dev
#### forge-gui-mobile-dev
Libgdx backend for desktop development for mobile backends. Utilizes LWJGL. Relies on forge-gui-mobile for GUI logic.

View File

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

View File

@@ -18,6 +18,7 @@
package forge.ai;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.common.base.Predicate;
@@ -43,6 +44,7 @@ import forge.game.combat.GlobalAttackRestrictions;
import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordInterface;
import forge.game.player.Player;
import forge.game.player.PlayerPredicates;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
@@ -54,7 +56,6 @@ import forge.util.TextUtil;
import forge.util.collect.FCollectionView;
//doesHumanAttackAndWin() uses the global variable AllZone.getComputerPlayer()
/**
* <p>
* ComputerUtil_Attack2 class.
@@ -91,7 +92,7 @@ public class AiAttackController {
public AiAttackController(final Player ai, boolean nextTurn) {
this.ai = ai;
this.defendingOpponent = choosePreferredDefenderPlayer();
this.defendingOpponent = choosePreferredDefenderPlayer(ai);
this.oppList = getOpponentCreatures(this.defendingOpponent);
this.myList = ai.getCreaturesInPlay();
this.attackers = new ArrayList<>();
@@ -107,7 +108,7 @@ public class AiAttackController {
public AiAttackController(final Player ai, Card attacker) {
this.ai = ai;
this.defendingOpponent = choosePreferredDefenderPlayer();
this.defendingOpponent = choosePreferredDefenderPlayer(ai);
this.oppList = getOpponentCreatures(this.defendingOpponent);
this.myList = ai.getCreaturesInPlay();
this.attackers = new ArrayList<>();
@@ -156,13 +157,12 @@ public class AiAttackController {
}
/** Choose opponent for AI to attack here. Expand as necessary. */
private Player choosePreferredDefenderPlayer() {
Player defender = ai.getWeakestOpponent(); //Gets opponent with the least life
public static Player choosePreferredDefenderPlayer(Player ai) {
Player defender = ai.getWeakestOpponent(); //Concentrate on opponent within easy kill range
if (defender.getLife() < 8) { //Concentrate on opponent within easy kill range
return defender;
} else { //Otherwise choose a random opponent to ensure no ganging up on players
defender = ai.getOpponents().get(MyRandom.getRandom().nextInt(ai.getOpponents().size()));
if (defender.getLife() > 8) { //Otherwise choose a random opponent to ensure no ganging up on players
// TODO should we cache the random for each turn? some functions like shouldPumpCard base their decisions on the assumption who will be attacked
return ai.getOpponents().get(MyRandom.getRandom().nextInt(ai.getOpponents().size()));
}
return defender;
}
@@ -393,7 +393,7 @@ public class AiAttackController {
//Calculate the amount of creatures necessary
for (int i = 0; i < list.size(); i++) {
if (!this.doesHumanAttackAndWin(ai, i)) {
if (!doesHumanAttackAndWin(ai, i)) {
blockersNeeded = i;
break;
}
@@ -413,12 +413,10 @@ public class AiAttackController {
final Player opp = this.defendingOpponent;
// Increase the total number of blockers needed by 1 if Finest Hour in
// play
// Increase the total number of blockers needed by 1 if Finest Hour in play
// (human will get an extra first attack with a creature that untaps)
// In addition, if the computer guesses it needs no blockers, make sure
// that
// it won't be surprised by Exalted
// that it won't be surprised by Exalted
final int humanExaltedBonus = opp.countExaltedBonus();
if (humanExaltedBonus > 0) {
@@ -428,8 +426,7 @@ public class AiAttackController {
// total attack = biggest creature + exalted, *2 if Rafiq is in play
int humanBasePower = getAttack(this.oppList.get(0)) + humanExaltedBonus;
if (finestHour) {
// For Finest Hour, one creature could attack and get the
// bonus TWICE
// For Finest Hour, one creature could attack and get the bonus TWICE
humanBasePower = humanBasePower + humanExaltedBonus;
}
final int totalExaltedAttack = opp.isCardInPlay("Rafiq of the Many") ? 2 * humanBasePower
@@ -450,7 +447,6 @@ public class AiAttackController {
return notNeededAsBlockers;
}
// this uses a global variable, which isn't perfect
public final boolean doesHumanAttackAndWin(final Player ai, final int nBlockingCreatures) {
int totalAttack = 0;
int totalPoison = 0;
@@ -624,7 +620,7 @@ public class AiAttackController {
int totalCombatDamage = ComputerUtilCombat.sumDamageIfUnblocked(unblockedAttackers, opp) + trampleDamage;
int totalPoisonDamage = ComputerUtilCombat.sumPoisonIfUnblocked(unblockedAttackers, opp);
if (totalCombatDamage + ComputerUtil.possibleNonCombatDamage(ai) >= opp.getLife()
if (totalCombatDamage + ComputerUtil.possibleNonCombatDamage(ai, opp) >= opp.getLife()
&& !((opp.cantLoseForZeroOrLessLife() || ai.cantWin()) && opp.getLife() < 1)) {
return true;
}
@@ -641,7 +637,7 @@ public class AiAttackController {
if (defs.size() == 1) {
return defs.getFirst();
}
Player prefDefender = (Player) (defs.contains(this.defendingOpponent) ? this.defendingOpponent : defs.get(0));
GameEntity prefDefender = defs.contains(this.defendingOpponent) ? this.defendingOpponent : defs.get(0);
// Attempt to see if there's a defined entity that must be attacked strictly this turn...
GameEntity entity = ai.getMustAttackEntityThisTurn();
@@ -665,7 +661,8 @@ public class AiAttackController {
// 2. attack planeswalkers
List<Card> pwDefending = c.getDefendingPlaneswalkers();
if (!pwDefending.isEmpty()) {
return pwDefending.get(0);
final Card pwNearUlti = ComputerUtilCard.getBestPlaneswalkerToDamage(pwDefending);
return pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending);
} else {
return prefDefender;
}
@@ -682,7 +679,6 @@ public class AiAttackController {
* @return a {@link forge.game.combat.Combat} object.
*/
public final void declareAttackers(final Combat combat) {
if (this.attackers.isEmpty()) {
return;
}
@@ -702,14 +698,14 @@ public class AiAttackController {
tradeIfLowerLifePressure = aic.getBooleanProperty(AiProps.RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE);
}
final boolean bAssault = this.doAssault(ai);
final boolean bAssault = doAssault(ai);
// TODO: detect Lightmine Field by presence of a card with a specific trigger
final boolean lightmineField = ComputerUtilCard.isPresentOnBattlefield(ai.getGame(), "Lightmine Field");
// TODO: detect Season of the Witch by presence of a card with a specific trigger
final boolean seasonOfTheWitch = ComputerUtilCard.isPresentOnBattlefield(ai.getGame(), "Season of the Witch");
// Determine who will be attacked
GameEntity defender = this.chooseDefender(combat, bAssault);
GameEntity defender = chooseDefender(combat, bAssault);
List<Card> attackersLeft = new ArrayList<>(this.attackers);
// TODO probably use AttackConstraints instead of only GlobalAttackRestrictions?
@@ -717,7 +713,7 @@ public class AiAttackController {
int attackMax = restrict.getMax();
if (attackMax == -1) {
// check with the local limitations vs. the chosen defender
attackMax = ComputerUtilCombat.getMaxAttackersFor(defender);
attackMax = restrict.getDefenderMax().get(defender) == null ? -1 : restrict.getDefenderMax().get(defender);
}
if (attackMax == 0) {
@@ -777,7 +773,6 @@ public class AiAttackController {
return;
}
if (bAssault) {
if (LOG_AI_ATTACKS)
System.out.println("Assault");
@@ -852,7 +847,6 @@ public class AiAttackController {
return;
}
// *******************
// Evaluate the creature forces
// *******************
@@ -919,7 +913,7 @@ public class AiAttackController {
// find the potential damage ratio the AI can cause
double humanLifeToDamageRatio = 1000000;
if (candidateUnblockedDamage > 0) {
humanLifeToDamageRatio = (double) (opp.getLife() - ComputerUtil.possibleNonCombatDamage(ai)) / candidateUnblockedDamage;
humanLifeToDamageRatio = (double) (opp.getLife() - ComputerUtil.possibleNonCombatDamage(ai, opp)) / candidateUnblockedDamage;
}
// determine if the ai outnumbers the player
@@ -936,12 +930,9 @@ public class AiAttackController {
// *********************
// if outnumber and superior ratio work out whether attritional all out
// attacking will work
// attritional attack will expect some creatures to die but to achieve
// victory by sheer weight
// of numbers attacking turn after turn. It's not calculate very
// carefully, the accuracy
// can probably be improved
// attacking will work attritional attack will expect some creatures to die but to achieve
// victory by sheer weight of numbers attacking turn after turn. It's not calculate very
// carefully, the accuracy can probably be improved
// *********************
boolean doAttritionalAttack = false;
// get list of attackers ordered from low power to high
@@ -975,7 +966,6 @@ public class AiAttackController {
doAttritionalAttack = true;
}
}
// System.out.println(doAttritionalAttack + " = do attritional attack");
// *********************
// end attritional attack calculation
// *********************
@@ -1025,11 +1015,9 @@ public class AiAttackController {
// end see how long until unblockable attackers will be fatal
// *****************
// decide on attack aggression based on a comparison of forces, life
// totals and other considerations
// some bad "magic numbers" here, TODO replace with nice descriptive
// variable names
// totals and other considerations some bad "magic numbers" here
// TODO replace with nice descriptive variable names
if (ratioDiff > 0 && doAttritionalAttack) {
this.aiAggression = 5; // attack at all costs
} else if ((ratioDiff >= 1 && this.attackers.size() > 1 && (humanLifeToDamageRatio < 2 || outNumber > 0))
@@ -1105,17 +1093,18 @@ public class AiAttackController {
// if enough damage: switch to next planeswalker or player
if (damage >= pw.getCounters(CounterEnumType.LOYALTY)) {
List<Card> pwDefending = combat.getDefendingPlaneswalkers();
boolean found = false;
// look for next planeswalker
for (Card walker : pwDefending) {
if (combat.getAttackersOf(walker).isEmpty()) {
defender = walker;
found = true;
break;
for (Card walker : Lists.newArrayList(pwDefending)) {
if (!combat.getAttackersOf(walker).isEmpty()) {
pwDefending.remove(walker);
}
}
if (!found) {
defender = combat.getDefendingPlayers().get(0);
if (pwDefending.isEmpty()) {
defender = Collections.min(Lists.newArrayList(combat.getDefendingPlayers()), PlayerPredicates.compareByLife());
}
else {
final Card pwNearUlti = ComputerUtilCard.getBestPlaneswalkerToDamage(pwDefending);
defender = pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending);
}
}
}
@@ -1224,9 +1213,8 @@ public class AiAttackController {
}
// look at the attacker in relation to the blockers to establish a
// number of factors about the attacking
// context that will be relevant to the attackers decision according to
// the selected strategy
// number of factors about the attacking context that will be relevant
// to the attackers decision according to the selected strategy
for (final Card defender : validBlockers) {
// if both isWorthLessThanAllKillers and canKillAllDangerous are false there's nothing more to check
if (isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2) {
@@ -1300,14 +1288,11 @@ public class AiAttackController {
}
if (numberOfPossibleBlockers > 2
|| (numberOfPossibleBlockers >= 1 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 1, combat))
|| (numberOfPossibleBlockers == 2 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 2, combat))) {
|| (numberOfPossibleBlockers >= 1 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 1, this.defendingOpponent))
|| (numberOfPossibleBlockers == 2 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 2, this.defendingOpponent))) {
canBeBlocked = true;
}
/*System.out.println(attacker + " canBeKilledByOne: " + canBeKilledByOne + " canKillAll: "
+ canKillAll + " isWorthLessThanAllKillers: " + isWorthLessThanAllKillers + " canBeBlocked: " + canBeBlocked);*/
// decide if the creature should attack based on the prevailing strategy
// choice in aiAggression
// decide if the creature should attack based on the prevailing strategy choice in aiAggression
switch (this.aiAggression) {
case 6: // Exalted: expecting to at least kill a creature of equal value or not be blocked
if ((canKillAll && isWorthLessThanAllKillers) || !canBeBlocked) {
@@ -1373,8 +1358,7 @@ public class AiAttackController {
}
// if card has a Exert Trigger which would target,
// but there are no creatures it can target, no need to exert with
// it
// but there are no creatures it can target, no need to exert with it
boolean missTarget = false;
for (Trigger t : c.getTriggers()) {
if (!TriggerType.Exerted.equals(t.getMode())) {
@@ -1442,7 +1426,7 @@ public class AiAttackController {
public String toProtectAttacker(SpellAbility sa) {
//AiAttackController is created with the selected attacker as the only entry in "attackers"
if (sa.getApi() != ApiType.Protection || oppList.isEmpty() || getPossibleBlockers(oppList, attackers).isEmpty()) {
return null; //not protection sa or attacker is already unblockable
return null; //not protection sa or attacker is already unblockable
}
final List<String> choices = ProtectEffect.getProtectionList(sa);
String color = ComputerUtilCard.getMostProminentColor(getPossibleBlockers(oppList, attackers)), artifact = null;
@@ -1452,7 +1436,7 @@ public class AiAttackController {
if (!choices.contains(color)) {
color = null;
}
for (Card c : oppList) { //find a blocker that ignores the currently selected protection
for (Card c : oppList) { //find a blocker that ignores the currently selected protection
if (artifact != null && !c.isArtifact()) {
artifact = null;
}
@@ -1485,7 +1469,7 @@ public class AiAttackController {
break;
}
}
if (color == null && artifact == null) { //nothing can make the attacker unblockable
if (color == null && artifact == null) { //nothing can make the attacker unblockable
return null;
}
}
@@ -1563,4 +1547,4 @@ public class AiAttackController {
return true;
}
} // end class ComputerUtil_Attack2
}

View File

@@ -180,11 +180,9 @@ public class AiBlockController {
// Good Blocks means a good trade or no trade
private void makeGoodBlocks(final Combat combat) {
List<Card> currentAttackers = new ArrayList<>(attackersLeft);
for (final Card attacker : attackersLeft) {
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")
|| attacker.hasKeyword(Keyword.MENACE)) {
@@ -192,7 +190,6 @@ public class AiBlockController {
}
Card blocker = null;
final List<Card> blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
final List<Card> safeBlockers = getSafeBlockers(combat, attacker, blockers);
@@ -305,7 +302,6 @@ public class AiBlockController {
}
Card blocker = null;
final List<Card> blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
for (Card b : blockers) {
@@ -366,10 +362,9 @@ public class AiBlockController {
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
// if the total damage of the blockgang was not enough
// without but is enough with this blocker finish the
// blockgang
// without but is enough with this blocker finish the blockgang
if (ComputerUtilCombat.totalFirstStrikeDamageOfBlockers(attacker, blockGang) < damageNeeded
|| CombatUtil.needsBlockers(attacker) > blockGang.size()) {
|| CombatUtil.getMinNumBlockersForAttacker(attacker, ai) > blockGang.size()) {
blockGang.add(blocker);
if (ComputerUtilCombat.totalFirstStrikeDamageOfBlockers(attacker, blockGang) >= damageNeeded) {
currentAttackers.remove(attacker);
@@ -407,7 +402,7 @@ public class AiBlockController {
boolean foundDoubleBlock = false; // if true, a good double block is found
// AI can't handle good blocks with more than three creatures yet
if (CombatUtil.needsBlockers(attacker) > (considerTripleBlock ? 3 : 2)) {
if (CombatUtil.getMinNumBlockersForAttacker(attacker, ai) > (considerTripleBlock ? 3 : 2)) {
continue;
}
@@ -444,7 +439,7 @@ public class AiBlockController {
final int addedValue = ComputerUtilCard.evaluateCreature(blocker);
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
if ((damageNeeded > currentDamage || CombatUtil.needsBlockers(attacker) > blockGang.size())
if ((damageNeeded > currentDamage || CombatUtil.getMinNumBlockersForAttacker(attacker, ai) > blockGang.size())
&& !(damageNeeded > currentDamage + additionalDamage)
// The attacker will be killed
&& (absorbedDamage2 + absorbedDamage > attacker.getNetCombatDamage()
@@ -454,8 +449,7 @@ public class AiBlockController {
|| (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)))
// or life is in danger
&& CombatUtil.canBlock(attacker, blocker, combat)) {
// this is needed for attackers that can't be blocked by
// more than 1
// this is needed for attackers that can't be blocked by more than 1
currentAttackers.remove(attacker);
combat.addBlocker(attacker, blocker);
if (CombatUtil.canBlock(attacker, leader, combat)) {
@@ -496,7 +490,7 @@ public class AiBlockController {
final int addedValue3 = ComputerUtilCard.evaluateCreature(secondBlocker);
final int netCombatDamage = attacker.getNetCombatDamage();
if ((damageNeeded > currentDamage || CombatUtil.needsBlockers(attacker) > blockGang.size())
if ((damageNeeded > currentDamage || CombatUtil.getMinNumBlockersForAttacker(attacker, ai) > blockGang.size())
&& !(damageNeeded > currentDamage + additionalDamage2 + additionalDamage3)
// The attacker will be killed
&& ((absorbedDamage2 + absorbedDamage > netCombatDamage && absorbedDamage3 + absorbedDamage > netCombatDamage
@@ -510,8 +504,7 @@ public class AiBlockController {
// or life is in danger
&& CombatUtil.canBlock(attacker, secondBlocker, combat)
&& CombatUtil.canBlock(attacker, thirdBlocker, combat)) {
// this is needed for attackers that can't be blocked by
// more than 1
// this is needed for attackers that can't be blocked by more than 1
currentAttackers.remove(attacker);
combat.addBlocker(attacker, thirdBlocker);
if (CombatUtil.canBlock(attacker, secondBlocker, combat)) {
@@ -587,12 +580,10 @@ public class AiBlockController {
* @param combat a {@link forge.game.combat.Combat} object.
*/
private void makeTradeBlocks(final Combat combat) {
List<Card> currentAttackers = new ArrayList<>(attackersLeft);
List<Card> killingBlockers;
for (final Card attacker : attackersLeft) {
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|| attacker.hasKeyword(Keyword.MENACE)
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
@@ -628,7 +619,6 @@ public class AiBlockController {
// Chump Blocks (should only be made if life is in danger)
private void makeChumpBlocks(final Combat combat) {
List<Card> currentAttackers = new ArrayList<>(attackersLeft);
makeChumpBlocks(combat, currentAttackers);
@@ -639,7 +629,6 @@ public class AiBlockController {
}
private void makeChumpBlocks(final Combat combat, List<Card> attackers) {
if (attackers.isEmpty() || !ComputerUtilCombat.lifeInDanger(ai, combat)) {
return;
}
@@ -694,11 +683,9 @@ public class AiBlockController {
// Block creatures with "can't be blocked except by two or more creatures"
private void makeMultiChumpBlocks(final Combat combat) {
List<Card> currentAttackers = new ArrayList<>(attackersLeft);
for (final Card attacker : currentAttackers) {
if (!attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
&& !attacker.hasKeyword(Keyword.MENACE)
&& !attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
@@ -730,14 +717,12 @@ public class AiBlockController {
/** Reinforce blockers blocking attackers with trample (should only be made if life is in danger) */
private void reinforceBlockersAgainstTrample(final Combat combat) {
List<Card> chumpBlockers;
List<Card> tramplingAttackers = CardLists.getKeyword(attackers, Keyword.TRAMPLE);
tramplingAttackers = CardLists.filter(tramplingAttackers, Predicates.not(rampagesOrNeedsManyToBlock));
// TODO - should check here for a "rampage-like" trigger that replaced
// the keyword:
// TODO - should check here for a "rampage-like" trigger that replaced the keyword:
// "Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it."
for (final Card attacker : tramplingAttackers) {
@@ -764,7 +749,6 @@ public class AiBlockController {
/** Support blockers not destroying the attacker with more blockers to try to kill the attacker */
private void reinforceBlockersToKill(final Combat combat) {
List<Card> safeBlockers;
List<Card> blockers;
List<Card> targetAttackers = CardLists.filter(blockedButUnkilled, Predicates.not(rampagesOrNeedsManyToBlock));
@@ -1036,27 +1020,21 @@ public class AiBlockController {
} else {
lifeInDanger = false;
}
// if life is still in danger
// Reinforce blockers blocking attackers with trample if life is
// still
// in danger
// Reinforce blockers blocking attackers with trample if life is still in danger
if (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)) {
reinforceBlockersAgainstTrample(combat);
} else {
lifeInDanger = false;
}
// Support blockers not destroying the attacker with more blockers
// to
// try to kill the attacker
// to try to kill the attacker
if (!lifeInDanger) {
reinforceBlockersToKill(combat);
}
// == 2. If the AI life would still be in danger make a safer
// approach ==
// == 2. If the AI life would still be in danger make a safer approach ==
if (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)) {
clearBlockers(combat, possibleBlockers); // reset every block
// assignment
clearBlockers(combat, possibleBlockers); // reset every block assignment
makeTradeBlocks(combat); // choose necessary trade blocks
// if life is in danger
makeGoodBlocks(combat);
@@ -1066,8 +1044,7 @@ public class AiBlockController {
} else {
lifeInDanger = false;
}
// Reinforce blockers blocking attackers with trample if life is
// still in danger
// Reinforce blockers blocking attackers with trample if life is still in danger
if (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)) {
reinforceBlockersAgainstTrample(combat);
} else {
@@ -1077,11 +1054,9 @@ public class AiBlockController {
reinforceBlockersToKill(combat);
}
// == 3. If the AI life would be in serious danger make an even
// safer approach ==
// == 3. If the AI life would be in serious danger make an even safer approach ==
if (lifeInDanger && ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
clearBlockers(combat, possibleBlockers); // reset every block
// assignment
clearBlockers(combat, possibleBlockers); // reset every block assignment
makeChumpBlocks(combat); // choose chump blocks
if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
makeTradeBlocks(combat); // choose necessary trade
@@ -1090,15 +1065,13 @@ public class AiBlockController {
if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
makeGoodBlocks(combat);
}
// Reinforce blockers blocking attackers with trample if life is
// still in danger
// Reinforce blockers blocking attackers with trample if life is still in danger
else {
reinforceBlockersAgainstTrample(combat);
}
makeGangBlocks(combat);
// Support blockers not destroying the attacker with more
// blockers
// to try to kill the attacker
// blockers to try to kill the attacker
reinforceBlockersToKill(combat);
}
}
@@ -1108,7 +1081,7 @@ public class AiBlockController {
chumpBlockers.addAll(CardLists.getKeyword(blockersLeft, "CARDNAME blocks each combat if able."));
// if an attacker with lure attacks - all that can block
for (final Card blocker : blockersLeft) {
if (CombatUtil.mustBlockAnAttacker(blocker, combat)) {
if (CombatUtil.mustBlockAnAttacker(blocker, combat, null)) {
chumpBlockers.add(blocker);
}
}
@@ -1118,7 +1091,7 @@ public class AiBlockController {
blockers = getPossibleBlockers(combat, attacker, chumpBlockers, false);
for (final Card blocker : blockers) {
if (CombatUtil.canBlock(attacker, blocker, combat) && blockersLeft.contains(blocker)
&& (CombatUtil.mustBlockAnAttacker(blocker, combat)
&& (CombatUtil.mustBlockAnAttacker(blocker, combat, null)
|| blocker.hasKeyword("CARDNAME blocks each turn if able.")
|| blocker.hasKeyword("CARDNAME blocks each combat if able."))) {
combat.addBlocker(attacker, blocker);
@@ -1137,7 +1110,6 @@ public class AiBlockController {
}
}
// check to see if it's possible to defend a Planeswalker under attack with a chump block,
// unless life is low enough to be more worried about saving preserving the life total
if (ai.getController().isAI() && !ComputerUtilCombat.lifeInDanger(ai, combat)) {

View File

@@ -32,7 +32,9 @@ import com.google.common.collect.Lists;
import forge.ai.ability.ChangeZoneAi;
import forge.ai.ability.ExploreAi;
import forge.ai.ability.LearnAi;
import forge.ai.simulation.SpellAbilityPicker;
import forge.card.CardStateName;
import forge.card.MagicColor;
import forge.card.mana.ManaCost;
import forge.deck.CardPool;
@@ -45,6 +47,7 @@ import forge.game.Game;
import forge.game.GameActionUtil;
import forge.game.GameEntity;
import forge.game.GlobalRuleChange;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.SpellApiBased;
@@ -77,6 +80,7 @@ import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.replacement.ReplaceMoved;
import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementLayer;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.LandAbility;
@@ -88,6 +92,7 @@ import forge.game.spellability.SpellAbilityCondition;
import forge.game.spellability.SpellAbilityPredicates;
import forge.game.spellability.SpellPermanent;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityMustTarget;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.trigger.WrappedAbility;
@@ -305,6 +310,8 @@ public class AiController {
}
exSA.setTrigger(tr);
// need to set TriggeredObject
exSA.setTriggeringObject(AbilityKey.Card, card);
// for trigger test, need to ignore the conditions
SpellAbilityCondition cons = exSA.getConditions();
@@ -430,17 +437,22 @@ public class AiController {
}
}
// don't play the land if it has cycling and enough lands are available
final FCollectionView<SpellAbility> spellAbilities = c.getSpellAbilities();
final CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
CardCollection lands = new CardCollection(battlefield);
lands.addAll(hand);
lands = CardLists.filter(lands, CardPredicates.Presets.LANDS);
int maxCmcInHand = Aggregates.max(hand, CardPredicates.Accessors.fnGetCmc);
for (final SpellAbility sa : spellAbilities) {
if (sa.isCycling()) {
if (lands.size() >= Math.max(maxCmcInHand, 6)) {
if (lands.size() >= Math.max(maxCmcInHand, 6)) {
// don't play MDFC land if other side is spell and enough lands are available
if (!c.isLand() || (c.isModal() && !c.getState(CardStateName.Modal).getType().isLand())) {
return false;
}
// don't play the land if it has cycling and enough lands are available
final FCollectionView<SpellAbility> spellAbilities = c.getSpellAbilities();
for (final SpellAbility sa : spellAbilities) {
if (sa.isCycling()) {
return false;
}
}
@@ -498,23 +510,38 @@ public class AiController {
landList = unreflectedLands;
}
//try to skip lands that enter the battlefield tapped
if (!nonLandsInHand.isEmpty()) {
CardCollection nonTappeddLands = new CardCollection();
CardCollection nonTappedLands = new CardCollection();
for (Card land : landList) {
// Is this the best way to check if a land ETB Tapped?
if (land.hasSVar("ETBTappedSVar")) {
// check replacement effects if land would enter tapped or not
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(land);
repParams.put(AbilityKey.Origin, land.getZone().getZoneType());
repParams.put(AbilityKey.Destination, ZoneType.Battlefield);
repParams.put(AbilityKey.Source, land);
boolean foundTapped = false;
for (ReplacementEffect re : player.getGame().getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.Other)) {
SpellAbility reSA = re.ensureAbility();
if (reSA == null || !ApiType.Tap.equals(reSA.getApi())) {
continue;
}
reSA.setActivatingPlayer(reSA.getHostCard().getController());
if (reSA.metConditions()) {
foundTapped = true;
break;
}
}
// TODO if this is the only source for a color we need badly prioritize it instead
if (foundTapped) {
continue;
}
// Glacial Fortress and friends
if (land.hasSVar("ETBCheckSVar") && CardFactoryUtil.xCount(land, land.getSVar("ETBCheckSVar")) == 0) {
continue;
}
nonTappeddLands.add(land);
nonTappedLands.add(land);
}
if (!nonTappeddLands.isEmpty()) {
landList = nonTappeddLands;
if (!nonTappedLands.isEmpty()) {
landList = nonTappedLands;
}
}
@@ -586,7 +613,6 @@ public class AiController {
sa.setActivatingPlayer(player);
// check everything necessary
AiPlayDecision opinion = canPlayAndPayFor(currentSA);
//PhaseHandler ph = game.getPhaseHandler();
// System.out.printf("Ai thinks '%s' of %s @ %s %s >>> \n", opinion, sa, Lang.getPossesive(ph.getPlayerTurn().getName()), ph.getPhase());
@@ -787,10 +813,10 @@ public class AiController {
}
else {
Cost payCosts = sa.getPayCosts();
if(payCosts != null) {
if (payCosts != null) {
ManaCost mana = payCosts.getTotalMana();
if (mana != null) {
if(mana.countX() > 0) {
if (mana.countX() > 0) {
// Set PayX here to maximum value.
final int xPay = ComputerUtilCost.getMaxXValue(sa, player);
if (xPay <= 0) {
@@ -826,8 +852,11 @@ public class AiController {
}
return canPlayFromEffectAI((SpellPermanent)sa, false, true);
}
if (sa.usesTargeting() && !sa.isTargetNumberValid()) {
if (!sa.getTargetRestrictions().hasCandidates(sa, true)) {
if (sa.usesTargeting()) {
if (!sa.isTargetNumberValid() && !sa.getTargetRestrictions().hasCandidates(sa, true)) {
return AiPlayDecision.TargetingFailed;
}
if (!StaticAbilityMustTarget.meetsMustTargetRestriction(sa)) {
return AiPlayDecision.TargetingFailed;
}
}
@@ -1110,12 +1139,18 @@ public class AiController {
final CardCollection discardList = new CardCollection();
int count = 0;
if (sa != null) {
String logic = sa.getParamOrDefault("AILogic", "");
sourceCard = sa.getHostCard();
if ("Always".equals(sa.getParam("AILogic")) && !validCards.isEmpty()) {
if ("Always".equals(logic) && !validCards.isEmpty()) {
min = 1;
} else if ("VolrathsShapeshifter".equals(sa.getParam("AILogic"))) {
} else if (logic.startsWith("UnlessAtLife.")) {
int threshold = AbilityUtils.calculateAmount(sourceCard, logic.substring(logic.indexOf(".") + 1), sa);
if (player.getLife() <= threshold) {
min = 1;
}
} else if ("VolrathsShapeshifter".equals(logic)) {
return SpecialCardAi.VolrathsShapeshifter.targetBestCreature(player, sa);
} else if ("DiscardCMCX".equals(sa.getParam("AILogic"))) {
} else if ("DiscardCMCX".equals(logic)) {
final int cmc = sa.getXManaCostPaid();
CardCollection discards = CardLists.filter(player.getCardsIn(ZoneType.Hand), CardPredicates.hasCMC(cmc));
if (discards.isEmpty()) {
@@ -1290,15 +1325,6 @@ public class AiController {
}
public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) {
if (logic.equalsIgnoreCase("ProtectFriendly")) {
final Player controller = hostCard.getController();
if (affected instanceof Player) {
return !((Player) affected).isOpponentOf(controller);
}
if (affected instanceof Card) {
return !((Card) affected).getController().isOpponentOf(controller);
}
}
return true;
}
@@ -1328,7 +1354,7 @@ public class AiController {
public AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) {
int damage = ComputerUtil.getDamageForPlaying(player, spell);
if (damage >= player.getLife() && !player.cantLoseForZeroOrLessLife() && player.canLoseLife()) {
if (!mandatory && damage >= player.getLife() && !player.cantLoseForZeroOrLessLife() && player.canLoseLife()) {
return AiPlayDecision.CurseEffects;
}
@@ -1397,8 +1423,6 @@ public class AiController {
private List<SpellAbility> singleSpellAbilityList(SpellAbility sa) {
if (sa == null) { return null; }
// System.out.println("Chosen to play: " + sa);
final List<SpellAbility> abilities = Lists.newArrayList();
abilities.add(sa);
return abilities;
@@ -1443,6 +1467,13 @@ public class AiController {
if (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land)) {
final List<SpellAbility> abilities = Lists.newArrayList();
// TODO extend this logic to evaluate MDFC with both sides land
// this can only happen if its a MDFC land
if (!land.isLand()) {
land.setState(CardStateName.Modal, true);
land.setBackSide(true);
}
LandAbility la = new LandAbility(land, player, null);
la.setCardState(land.getCurrentState());
if (la.canPlay()) {
@@ -1693,10 +1724,8 @@ public class AiController {
for (int i = 0; i < numToExile; i++) {
Card chosen = null;
for (final Card c : grave) { // Exile noncreatures first in
// case we can revive. Might
// wanna do some additional
// checking here for Flashback
// and the like.
// case we can revive. Might wanna do some additional
// checking here for Flashback and the like.
if (!c.isCreature()) {
chosen = c;
break;
@@ -1735,12 +1764,21 @@ public class AiController {
* @param sa the sa
* @return true, if successful
*/
public final boolean aiShouldRun(final ReplacementEffect effect, final SpellAbility sa) {
public final boolean aiShouldRun(final ReplacementEffect effect, final SpellAbility sa, GameEntity affected) {
Card hostCard = effect.getHostCard();
if (hostCard.hasAlternateState()) {
hostCard = game.getCardState(hostCard);
}
if (effect.hasParam("AILogic") && effect.getParam("AILogic").equalsIgnoreCase("ProtectFriendly")) {
final Player controller = hostCard.getController();
if (affected instanceof Player) {
return !((Player) affected).isOpponentOf(controller);
}
if (affected instanceof Card) {
return !((Card) affected).getController().isOpponentOf(controller);
}
}
if (effect.hasParam("AICheckSVar")) {
System.out.println("aiShouldRun?" + sa);
final String svarToCheck = effect.getParam("AICheckSVar");
@@ -1755,7 +1793,7 @@ public class AiController {
compareTo = Integer.parseInt(strCmpTo);
} catch (final Exception ignored) {
if (sa == null) {
compareTo = CardFactoryUtil.xCount(hostCard, hostCard.getSVar(strCmpTo));
compareTo = AbilityUtils.calculateAmount(hostCard, hostCard.getSVar(strCmpTo), effect);
} else {
compareTo = AbilityUtils.calculateAmount(hostCard, hostCard.getSVar(strCmpTo), sa);
}
@@ -1765,7 +1803,7 @@ public class AiController {
int left = 0;
if (sa == null) {
left = CardFactoryUtil.xCount(hostCard, hostCard.getSVar(svarToCheck));
left = AbilityUtils.calculateAmount(hostCard, svarToCheck, effect);
} else {
left = AbilityUtils.calculateAmount(hostCard, svarToCheck, sa);
}
@@ -1781,7 +1819,7 @@ public class AiController {
// AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns)
List<SpellAbility> result = Lists.newArrayList();
for(SpellAbility sa : usableFromOpeningHand) {
for (SpellAbility sa : usableFromOpeningHand) {
// Is there a better way for the AI to decide this?
if (doTrigger(sa, false)) {
result.add(sa);
@@ -1792,7 +1830,7 @@ public class AiController {
SpellAbility saGemstones = null;
List<SpellAbility> toRemove = Lists.newArrayList();
for(SpellAbility sa : result) {
for (SpellAbility sa : result) {
String srcName = sa.getHostCard().getName();
if ("Gemstone Caverns".equals(srcName)) {
if (saGemstones == null)
@@ -1855,11 +1893,13 @@ public class AiController {
} else if ("LowestLoseLife".equals(logic)) {
return MyRandom.getRandom().nextInt(Math.min(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1;
} else if ("HighestLoseLife".equals(logic)) {
return MyRandom.getRandom().nextInt(Math.max(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1;
return Math.min(player.getLife() -1,MyRandom.getRandom().nextInt(Math.max(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1);
} else if ("HighestGetCounter".equals(logic)) {
return MyRandom.getRandom().nextInt(3);
} else if (source.hasSVar("EnergyToPay")) {
return AbilityUtils.calculateAmount(source, source.getSVar("EnergyToPay"), sa);
} else if ("Vermin".equals(logic)) {
return MyRandom.getRandom().nextInt(Math.max(player.getLife() - 5, 0));
}
return max;
}
@@ -1876,7 +1916,7 @@ public class AiController {
if (sa.hasParam("AIMaxAmount")) {
max = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("AIMaxAmount"), sa);
}
switch(sa.getApi()) {
switch (sa.getApi()) {
case TwoPiles:
// TODO: improve AI
Card biggest = null;
@@ -1954,7 +1994,6 @@ public class AiController {
return result;
}
// this is where the computer cheats
// changes AllZone.getComputerPlayer().getZone(Zone.Library)
@@ -2007,11 +2046,6 @@ public class AiController {
}
}
// check
for (int i = 0; i < library.size(); i++) {
System.out.println(library.get(i));
}
return library;
} // smoothComputerManaCurve()
@@ -2089,8 +2123,11 @@ public class AiController {
if (useSimulation) {
return simPicker.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player2, decider);
}
if (sa.getApi() == ApiType.Explore) {
return ExploreAi.shouldPutInGraveyard(fetchList, decider);
} else if (sa.getApi() == ApiType.Learn) {
return LearnAi.chooseCardToLearn(fetchList, decider, sa);
} else {
return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player2, decider);
}
@@ -2231,10 +2268,8 @@ public class AiController {
}
}
// AI logic for choosing which replacement effect to apply
// happens here.
// AI logic for choosing which replacement effect to apply happens here.
return Iterables.getFirst(list, null);
}
}

View File

@@ -6,9 +6,11 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import forge.game.cost.*;
import org.apache.commons.lang3.ObjectUtils;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.card.CardType;
@@ -22,43 +24,12 @@ import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.cost.CostAddMana;
import forge.game.cost.CostChooseCreatureType;
import forge.game.cost.CostDamage;
import forge.game.cost.CostDecisionMakerBase;
import forge.game.cost.CostDiscard;
import forge.game.cost.CostDraw;
import forge.game.cost.CostExert;
import forge.game.cost.CostExile;
import forge.game.cost.CostExileFromStack;
import forge.game.cost.CostExiledMoveToGrave;
import forge.game.cost.CostFlipCoin;
import forge.game.cost.CostGainControl;
import forge.game.cost.CostGainLife;
import forge.game.cost.CostMill;
import forge.game.cost.CostPartMana;
import forge.game.cost.CostPayEnergy;
import forge.game.cost.CostPayLife;
import forge.game.cost.CostPutCardToLib;
import forge.game.cost.CostPutCounter;
import forge.game.cost.CostRemoveAnyCounter;
import forge.game.cost.CostRemoveCounter;
import forge.game.cost.CostReturn;
import forge.game.cost.CostReveal;
import forge.game.cost.CostSacrifice;
import forge.game.cost.CostTap;
import forge.game.cost.CostTapType;
import forge.game.cost.CostUnattach;
import forge.game.cost.CostUntap;
import forge.game.cost.CostUntapType;
import forge.game.cost.PaymentDecision;
import forge.game.keyword.Keyword;
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.Localizer;
import forge.util.TextUtil;
import forge.util.collect.FCollectionView;
@@ -92,15 +63,9 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override
public PaymentDecision visit(CostChooseCreatureType cost) {
Integer amount = cost.convertAmount();
Iterable<String> choices = player.getController().chooseSomeType(
Localizer.getInstance().getMessage("lblCreature"), ability, amount, amount, Lists.newArrayList(CardType.getAllCreatureTypes()));
if (choices == null || Iterables.isEmpty(choices)) {
return null;
}
return PaymentDecision.types(choices);
String choice = player.getController().chooseSomeType("Creature", ability, CardType.getAllCreatureTypes(),
Lists.newArrayList());
return PaymentDecision.type(choice);
}
@Override
@@ -302,6 +267,15 @@ public class AiCostDecision extends CostDecisionMakerBase {
return PaymentDecision.number(c);
}
@Override
public PaymentDecision visit(CostRollDice cost) {
Integer c = cost.convertAmount();
if (c == null) {
c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
}
return PaymentDecision.number(c);
}
@Override
public PaymentDecision visit(CostGainControl cost) {
if (cost.payCostFromSource()) {
@@ -389,7 +363,6 @@ public class AiCostDecision extends CostDecisionMakerBase {
return PaymentDecision.number(c);
}
@Override
public PaymentDecision visit(CostPutCardToLib cost) {
if (cost.payCostFromSource()) {
@@ -569,7 +542,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
return null;
}
if (cost.getRevealFrom().equals(ZoneType.Exile)) {
if (cost.getRevealFrom().get(0).equals(ZoneType.Exile)) {
hand = CardLists.getValidCards(hand, type.split(";"), player, source, ability);
return PaymentDecision.card(getBestCreatureAI(hand));
}
@@ -583,6 +556,11 @@ public class AiCostDecision extends CostDecisionMakerBase {
return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability));
}
@Override
public PaymentDecision visit(CostRevealChosenPlayer cost) {
return PaymentDecision.number(1);
}
protected int removeCounter(GameEntityCounterTable table, List<Card> prefs, CounterEnumType cType, int stillToRemove) {
int removed = 0;
if (!prefs.isEmpty() && stillToRemove > 0) {
@@ -596,7 +574,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int thisRemove = Math.min(prefCard.getCounters(cType), stillToRemove);
if (thisRemove > 0) {
removed += thisRemove;
table.put(prefCard, CounterType.get(cType), thisRemove);
table.put(null, prefCard, CounterType.get(cType), thisRemove);
}
}
}
@@ -607,7 +585,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
public PaymentDecision visit(CostRemoveAnyCounter cost) {
final String amount = cost.getAmount();
final int c = AbilityUtils.calculateAmount(source, amount, ability);
final Card originalHost = ability.getOriginalOrHost();
final Card originalHost = ObjectUtils.defaultIfNull(ability.getOriginalHost(), source);
if (c <= 0) {
return null;
@@ -662,7 +640,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int thisRemove = Math.min(card.getCounters(ctype), c - toRemove);
if (thisRemove > 0) {
toRemove += thisRemove;
table.put(card, ctype, thisRemove);
table.put(null, card, ctype, thisRemove);
}
}
}
@@ -690,7 +668,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int over = Math.min(e.getValue(), c - toRemove);
if (over > 0) {
toRemove += over;
table.put(crd, e.getKey(), over);
table.put(null, crd, e.getKey(), over);
}
}
}
@@ -720,7 +698,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int over = Math.min(e.getValue(), c - toRemove);
if (over > 0) {
toRemove += over;
table.put(crd, e.getKey(), over);
table.put(null, crd, e.getKey(), over);
}
}
}
@@ -760,7 +738,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int over = Math.min(crd.getCounters(CounterEnumType.QUEST) - e, c - toRemove);
if (over > 0) {
toRemove += over;
table.put(crd, CounterType.get(CounterEnumType.QUEST), over);
table.put(null, crd, CounterType.get(CounterEnumType.QUEST), over);
}
}
}
@@ -784,7 +762,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int thisRemove = Math.min(card.getCounters(cost.counter), c - toRemove);
if (thisRemove > 0) {
toRemove += thisRemove;
table.put(card, cost.counter, thisRemove);
table.put(null, card, cost.counter, thisRemove);
}
}
}
@@ -798,7 +776,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
int thisRemove = Math.min(e.getValue(), c - toRemove);
if (thisRemove > 0) {
toRemove += thisRemove;
table.put(card, e.getKey(), thisRemove);
table.put(null, card, e.getKey(), thisRemove);
}
}
}

View File

@@ -188,7 +188,6 @@ public class ComputerUtil {
final Card source = sa.getHostCard();
final TargetRestrictions tgt = sa.getTargetRestrictions();
// Play higher costing spells first?
final Cost cost = sa.getPayCosts();
@@ -213,7 +212,8 @@ public class ComputerUtil {
if (unless != null && !unless.endsWith(">")) {
final int amount = AbilityUtils.calculateAmount(source, unless, sa);
final int usableManaSources = ComputerUtilMana.getAvailableManaSources(ComputerUtil.getOpponentFor(ai), true).size();
// this is enough as long as the AI is only smart enough to target top of stack
final int usableManaSources = ComputerUtilMana.getAvailableManaSources(ComputerUtilAbility.getTopSpellAbilityOnStack(ai.getGame(), sa).getActivatingPlayer(), true).size();
// If the Unless isn't enough, this should be less likely to be used
if (amount > usableManaSources) {
@@ -280,7 +280,7 @@ public class ComputerUtil {
SpellAbility newSA = sa.copyWithNoManaCost();
newSA.setActivatingPlayer(ai);
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA)) {
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA) || !ComputerUtilMana.canPayManaCost(newSA, ai, 0)) {
return false;
}
@@ -331,11 +331,14 @@ public class ComputerUtil {
}
public static Card getCardPreference(final Player ai, final Card activate, final String pref, final CardCollection typeList) {
return getCardPreference(ai, activate, pref, typeList, null);
}
public static Card getCardPreference(final Player ai, final Card activate, final String pref, final CardCollection typeList, SpellAbility sa) {
final Game game = ai.getGame();
String prefDef = "";
if (activate != null) {
prefDef = activate.getSVar("AIPreference");
final String[] prefGroups = activate.getSVar("AIPreference").split("\\|");
final String[] prefGroups = prefDef.split("\\|");
for (String prefGroup : prefGroups) {
final String[] prefValid = prefGroup.trim().split("\\$");
if (prefValid[0].equals(pref) && !prefValid[1].startsWith("Special:")) {
@@ -346,8 +349,8 @@ public class ComputerUtil {
for (String validItem : prefValid[1].split(",")) {
final CardCollection prefList = CardLists.getValidCards(typeList, validItem, activate.getController(), activate, null);
int threshold = getAIPreferenceParameter(activate, "CreatureEvalThreshold");
int minNeeded = getAIPreferenceParameter(activate, "MinCreaturesBelowThreshold");
int threshold = getAIPreferenceParameter(activate, "CreatureEvalThreshold", sa);
int minNeeded = getAIPreferenceParameter(activate, "MinCreaturesBelowThreshold", sa);
if (threshold != -1) {
List<Card> toRemove = Lists.newArrayList();
@@ -390,7 +393,7 @@ public class ComputerUtil {
final CardCollection sacMeList = CardLists.filter(typeList, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return (c.hasSVar("SacMe") && (Integer.parseInt(c.getSVar("SacMe")) == priority));
return c.hasSVar("SacMe") && (Integer.parseInt(c.getSVar("SacMe")) == priority);
}
});
if (!sacMeList.isEmpty()) {
@@ -419,6 +422,7 @@ public class ComputerUtil {
if (!nonCreatures.isEmpty()) {
return ComputerUtilCard.getWorstAI(nonCreatures);
} else if (!typeList.isEmpty()) {
// TODO make sure survival is possible in case the creature blocks a trampler
return ComputerUtilCard.getWorstAI(typeList);
}
}
@@ -505,7 +509,7 @@ public class ComputerUtil {
return null;
}
public static int getAIPreferenceParameter(final Card c, final String paramName) {
public static int getAIPreferenceParameter(final Card c, final String paramName, SpellAbility sa) {
if (!c.hasSVar("AIPreferenceParams")) {
return -1;
}
@@ -520,7 +524,21 @@ public class ComputerUtil {
case "CreatureEvalThreshold":
// Threshold of 150 is just below the level of a 1/1 mana dork or a 2/2 baseline creature with no keywords
if (paramName.equals(parName)) {
return Integer.parseInt(parValue);
int num = 0;
try {
num = Integer.parseInt(parValue);
} catch (NumberFormatException nfe) {
String[] valParts = StringUtils.split(parValue, "/");
CardCollection foundCards = AbilityUtils.getDefinedCards(c, valParts[0], sa);
if (!foundCards.isEmpty()) {
num = ComputerUtilCard.evaluateCreature(foundCards.get(0));
}
valParts[0] = Integer.toString(num);
if (valParts.length > 1) {
num = AbilityUtils.doXMath(num, valParts[1], c, sa);
}
}
return num;
}
break;
case "MinCreaturesBelowThreshold":
@@ -543,9 +561,8 @@ public class ComputerUtil {
typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability));
if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't sacrifice the card we're pumping
}
// don't sacrifice the card we're pumping
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, ability, ai);
if (typeList.size() < amount) {
return null;
@@ -573,9 +590,8 @@ public class ComputerUtil {
final Card target, final int amount, SpellAbility sa) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, sa);
if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't exile the card we're pumping
}
// don't exile the card we're pumping
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, sa, ai);
if (typeList.size() < amount) {
return null;
@@ -594,9 +610,8 @@ public class ComputerUtil {
final Card target, final int amount, SpellAbility sa) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, sa);
if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't move the card we're pumping
}
// don't move the card we're pumping
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, sa, ai);
if (typeList.size() < amount) {
return null;
@@ -718,12 +733,10 @@ public class ComputerUtil {
}
public static CardCollection chooseReturnType(final Player ai, final String type, final Card activate, final Card target, final int amount, SpellAbility sa) {
final CardCollection typeList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), activate.getController(), activate, sa);
if ((target != null) && target.getController() == ai) {
// don't bounce the card we're pumping
typeList.remove(target);
}
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), activate.getController(), activate, sa);
// don't bounce the card we're pumping
typeList = ComputerUtilCost.paymentChoicesWithoutTargets(typeList, sa, ai);
if (typeList.size() < amount) {
return new CardCollection();
@@ -743,7 +756,7 @@ public class ComputerUtil {
CardCollection remaining = new CardCollection(cardlist);
final CardCollection sacrificed = new CardCollection();
final Card host = source.getHostCard();
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold");
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold", source);
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
@@ -1068,9 +1081,6 @@ public class ComputerUtil {
return true;
}
}
if (card.isEquipment() && buffedcard.isCreature() && CombatUtil.canAttack(buffedcard, ComputerUtil.getOpponentFor(ai))) {
return true;
}
if (card.isCreature()) {
if (buffedcard.hasKeyword(Keyword.SOULBOND) && !buffedcard.isPaired()) {
return true;
@@ -1093,8 +1103,8 @@ public class ComputerUtil {
} // BuffedBy
// get all cards the human controls with AntiBuffedBy
final CardCollectionView antibuffed = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield);
// there's a good chance AI will attack weak target
final CardCollectionView antibuffed = ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield);
for (Card buffedcard : antibuffed) {
if (buffedcard.hasSVar("AntiBuffedBy")) {
final String buffedby = buffedcard.getSVar("AntiBuffedBy");
@@ -1128,7 +1138,7 @@ public class ComputerUtil {
creatures2.add(creatures.get(i));
}
}
if (((creatures2.size() + CardUtil.getThisTurnCast("Creature.YouCtrl", vengevines.get(0)).size()) > 1)
if (((creatures2.size() + CardUtil.getThisTurnCast("Creature.YouCtrl", vengevines.get(0), null).size()) > 1)
&& card.isCreature() && card.getManaCost().getCMC() <= 3) {
return true;
}
@@ -1142,27 +1152,16 @@ public class ComputerUtil {
* @return true if it's OK to cast this Card for less than the max targets
*/
public static boolean shouldCastLessThanMax(final Player ai, final Card source) {
boolean ret = true;
if (source.getManaCost().countX() > 0) {
// If TargetMax is MaxTgts (i.e., an "X" cost), this is fine because AI is limited by mana available.
return ret;
} else {
// Otherwise, if life is possibly in danger, then this is fine.
Combat combat = new Combat(ComputerUtil.getOpponentFor(ai));
CardCollectionView attackers = ComputerUtil.getOpponentFor(ai).getCreaturesInPlay();
for (Card att : attackers) {
if (ComputerUtilCombat.canAttackNextTurn(att, ai)) {
combat.addAttacker(att, ComputerUtil.getOpponentFor(att.getController()));
}
}
AiBlockController aiBlock = new AiBlockController(ai);
aiBlock.assignBlockersForCombat(combat);
if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
// Otherwise, return false. Do not play now.
ret = false;
}
if (source.getXManaCostPaid() > 0) {
// If TargetMax is MaxTgts (i.e., an "X" cost), this is fine because AI is limited by payment resources available.
return true;
}
return ret;
if (aiLifeInDanger(ai, false, 0)) {
// Otherwise, if life is possibly in danger, then this is fine.
return true;
}
// do not play now.
return false;
}
/**
@@ -1266,8 +1265,8 @@ public class ComputerUtil {
}
}
// get all cards the human controls with AntiBuffedBy
final CardCollectionView antibuffed = ComputerUtil.getOpponentFor(ai).getCardsIn(ZoneType.Battlefield);
// there's a good chance AI will attack weak target
final CardCollectionView antibuffed = ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield);
for (Card buffedcard : antibuffed) {
if (buffedcard.hasSVar("AntiBuffedBy")) {
final String buffedby = buffedcard.getSVar("AntiBuffedBy");
@@ -1463,7 +1462,7 @@ public class ComputerUtil {
return false;
}
public static int possibleNonCombatDamage(Player ai) {
public static int possibleNonCombatDamage(Player ai, Player enemy) {
int damage = 0;
final CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
all.addAll(ai.getCardsActivableInExternalZones(true));
@@ -1483,7 +1482,6 @@ public class ComputerUtil {
if (tgt == null) {
continue;
}
final Player enemy = ComputerUtil.getOpponentFor(ai);
if (!sa.canTarget(enemy)) {
continue;
}
@@ -1556,7 +1554,7 @@ public class ComputerUtil {
Iterables.addAll(objects, ComputerUtil.predictThreatenedObjects(ai, sa, spell));
}
if (top) {
break; // only evaluate top-stack
break; // only evaluate top-stack
}
}
@@ -2256,42 +2254,40 @@ public class ComputerUtil {
return getCardsToDiscardFromOpponent(aiChooser, p, sa, validCards, min, max);
}
public static List<String> chooseSomeType(Player ai, String kindOfType, String logic, int min, int max, Collection<String> validTypes) {
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();
List<String> chosenList = Lists.newArrayList();
String chosen = "";
if (kindOfType.equals("Card")) {
String chosen = "";
// TODO
// computer will need to choose a type
// based on whether it needs a creature or land,
// otherwise, lib search for most common type left
// then, reveal chosenType to Human
// computer will need to choose a type based on whether it needs a creature or land,
// otherwise, lib search for most common type left then, reveal chosenType to Human
if (game.getPhaseHandler().is(PhaseType.UNTAP) && logic == null) { // Storage Matrix
double amount = 0;
for (String type : validTypes) {
CardCollection list = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.isType(type), Presets.TAPPED);
double i = type.equals("Creature") ? list.size() * 1.5 : list.size();
if (i > amount) {
amount = i;
chosen = type;
for (String type : CardType.getAllCardTypes()) {
if (!invalidTypes.contains(type)) {
CardCollection list = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.isType(type), Presets.TAPPED);
double i = type.equals("Creature") ? list.size() * 1.5 : list.size();
if (i > amount) {
amount = i;
chosen = type;
}
}
}
} else if (logic == "MostProminentInComputerDeck") {
chosen = ComputerUtilCard.getMostProminentType(ai.getAllCards(), validTypes);
}
if (StringUtils.isEmpty(chosen)) {
chosen = validTypes.isEmpty() ? "Creature" : Aggregates.random(validTypes);
}
chosenList.add(chosen);
} else if (kindOfType.equals("Creature")) {
String chosen = "";
if (logic != null) {
List <String> valid = Lists.newArrayList(validTypes);
List <String> valid = Lists.newArrayList(CardType.getAllCreatureTypes());
valid.removeAll(invalidTypes);
if (logic.equals("MostProminentOnBattlefield")) {
chosen = ComputerUtilCard.getMostProminentType(game.getCardsIn(ZoneType.Battlefield), valid);
@@ -2302,7 +2298,7 @@ public class ComputerUtil {
else if (logic.equals("MostProminentOppControls")) {
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
chosen = ComputerUtilCard.getMostProminentType(list, valid);
if (!CardType.isACreatureType(chosen) || !validTypes.contains(chosen)) {
if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) {
list = CardLists.filterControlledBy(game.getCardsInGame(), ai.getOpponents());
chosen = ComputerUtilCard.getMostProminentType(list, valid);
}
@@ -2314,17 +2310,16 @@ public class ComputerUtil {
chosen = ComputerUtilCard.getMostProminentType(ai.getCardsIn(ZoneType.Graveyard), valid);
}
}
if (!CardType.isACreatureType(chosen) || !validTypes.contains(chosen)) {
chosen = Iterables.getFirst(validTypes, null);
if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) {
chosen = "Sliver";
}
chosenList.add(chosen);
} else if (kindOfType.equals("Basic Land")) {
String chosen = "";
if (logic != null) {
if (logic.equals("MostProminentOppControls")) {
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
List<String> valid = Lists.newArrayList(validTypes);
List<String> valid = Lists.newArrayList(CardType.getBasicTypes());
valid.removeAll(invalidTypes);
chosen = ComputerUtilCard.getMostProminentType(list, valid);
} else if (logic.equals("MostNeededType")) {
@@ -2347,9 +2342,9 @@ public class ComputerUtil {
}
}
else if (logic.equals("ChosenLandwalk")) {
for (Card c : ai.getWeakestOpponent().getLandsInPlay()) {
for (Card c : AiAttackController.choosePreferredDefenderPlayer(ai).getLandsInPlay()) {
for (String t : c.getType()) {
if (validTypes.contains(t) && CardType.isABasicLandType(t)) {
if (!invalidTypes.contains(t) && CardType.isABasicLandType(t)) {
chosen = t;
break;
}
@@ -2358,28 +2353,16 @@ public class ComputerUtil {
}
}
if (!CardType.isABasicLandType(chosen) || !validTypes.contains(chosen)) {
chosen = Iterables.getFirst(validTypes, null);
}
chosenList.add(chosen);
// the only one with choosing two types is Illusionary Terrain
// and it needs other AI logic
while (chosenList.size() < min) {
validTypes.remove(chosen);
chosen = Iterables.getFirst(validTypes, null);
if (chosen == null) {
return Lists.newArrayList();
}
chosenList.add(chosen);
if (!CardType.isABasicLandType(chosen) || invalidTypes.contains(chosen)) {
chosen = "Island";
}
}
else if (kindOfType.equals("Land")) {
String chosen = "";
if (logic != null) {
if (logic.equals("ChosenLandwalk")) {
for (Card c : ai.getWeakestOpponent().getLandsInPlay()) {
for (Card c : AiAttackController.choosePreferredDefenderPlayer(ai).getLandsInPlay()) {
for (String t : c.getType().getLandTypes()) {
if (validTypes.contains(t)) {
if (!invalidTypes.contains(t)) {
chosen = t;
break;
}
@@ -2388,11 +2371,10 @@ public class ComputerUtil {
}
}
if (StringUtils.isEmpty(chosen)) {
chosen = Iterables.getFirst(validTypes, null);
chosen = "Island";
}
chosenList.add(chosen);
}
return chosenList;
return chosen;
}
public static Object vote(Player ai, List<Object> options, SpellAbility sa, Multimap<Object, Player> votes, Player forPlayer) {
@@ -2413,23 +2395,26 @@ public class ComputerUtil {
case "Torture":
return "Torture";
case "GraceOrCondemnation":
return ai.getCreaturesInPlay().size() > ComputerUtil.getOpponentFor(ai).getCreaturesInPlay().size() ? "Grace"
: "Condemnation";
List<ZoneType> graceZones = new ArrayList<ZoneType>();
graceZones.add(ZoneType.Battlefield);
graceZones.add(ZoneType.Graveyard);
CardCollection graceCreatures = CardLists.getType(sa.getHostCard().getGame().getCardsIn(graceZones), "Creature");
int humanGrace = CardLists.filterControlledBy(graceCreatures, ai.getOpponents()).size();
int aiGrace = CardLists.filterControlledBy(graceCreatures, ai).size();
return aiGrace > humanGrace ? "Grace" : "Condemnation";
case "CarnageOrHomage":
CardCollection cardsInPlay = CardLists
.getNotType(sa.getHostCard().getGame().getCardsIn(ZoneType.Battlefield), "Land");
CardCollection cardsInPlay = CardLists.getNotType(sa.getHostCard().getGame().getCardsIn(ZoneType.Battlefield), "Land");
CardCollection humanlist = CardLists.filterControlledBy(cardsInPlay, ai.getOpponents());
CardCollection computerlist = CardLists.filterControlledBy(cardsInPlay, ai);
return (ComputerUtilCard.evaluatePermanentList(computerlist) + 3) < ComputerUtilCard
.evaluatePermanentList(humanlist) ? "Carnage" : "Homage";
CardCollection computerlist = ai.getCreaturesInPlay();
return (ComputerUtilCard.evaluatePermanentList(computerlist) + 3) < ComputerUtilCard.evaluatePermanentList(humanlist) ? "Carnage" : "Homage";
case "Judgment":
if (votes.isEmpty()) {
CardCollection list = new CardCollection();
for (Object o : options) {
if (o instanceof Card) {
list.add((Card) o);
}
}
}
return ComputerUtilCard.getBestAI(list);
} else {
return Iterables.getFirst(votes.keySet(), null);
@@ -2460,8 +2445,7 @@ public class ComputerUtil {
if (!source.canReceiveCounters(p1p1Type)) {
return opponent ? "Feather" : "Quill";
}
// if source is not on the battlefield anymore, choose +1/+1
// ones
// if source is not on the battlefield anymore, choose +1/+1 ones
if (!game.getCardState(source).isInZone(ZoneType.Battlefield)) {
return opponent ? "Feather" : "Quill";
}
@@ -2503,8 +2487,7 @@ public class ComputerUtil {
return opponent ? "Numbers" : "Strength";
}
// TODO check for ETB to +1/+1 counters
// or over another trigger like lifegain
// TODO check for ETB to +1/+1 counters or over another trigger like lifegain
int tokenScore = ComputerUtilCard.evaluateCreature(token);
@@ -2582,8 +2565,7 @@ public class ComputerUtil {
return "Taxes";
} else {
// ai is first voter or ally of controller
// both are not affected, but if opponents controll creatures,
// sacrifice is worse
// both are not affected, but if opponents control creatures, sacrifice is worse
return controller.getOpponents().getCreaturesInPlay().isEmpty() ? "Taxes" : "Death";
}
default:
@@ -2667,7 +2649,7 @@ public class ComputerUtil {
continue;
}
if (trigger.hasParam("ValidCard")) {
if (!card.isValid(trigger.getParam("ValidCard"), source.getController(), source, sa)) {
if (!card.isValid(trigger.getParam("ValidCard").split(","), source.getController(), source, sa)) {
continue;
}
}
@@ -2880,7 +2862,6 @@ public class ComputerUtil {
}
public static boolean lifegainNegative(final Player player, final Card source, final int n) {
if (!player.canGainLife()) {
return false;
}
@@ -2948,23 +2929,6 @@ public class ComputerUtil {
return true;
}
@Deprecated
public static final Player getOpponentFor(final Player player) {
// This method is deprecated and currently functions as a synonym for player.getWeakestOpponent
// until it can be replaced everywhere in the code.
// Consider replacing calls to this method either with a multiplayer-friendly determination of
// opponent that contextually makes the most sense, or with a direct call to player.getWeakestOpponent
// where that is applicable and makes sense from the point of view of multiplayer AI logic.
Player opponent = player.getWeakestOpponent();
if (opponent != null) {
return opponent;
}
throw new IllegalStateException("No opponents left ingame for " + player);
}
public static int countUsefulCreatures(Player p) {
CardCollection creats = p.getCreaturesInPlay();
int count = 0;
@@ -3047,33 +3011,34 @@ public class ComputerUtil {
// call this to determine if it's safe to use a life payment spell
// or trigger "emergency" strategies such as holding mana for Spike Weaver of Counterspell.
public static boolean aiLifeInDanger(Player ai, boolean serious, int payment) {
Player opponent = ComputerUtil.getOpponentFor(ai);
// test whether the human can kill the ai next turn
Combat combat = new Combat(opponent);
boolean containsAttacker = false;
for (Card att : opponent.getCreaturesInPlay()) {
if (ComputerUtilCombat.canAttackNextTurn(att, ai)) {
combat.addAttacker(att, ai);
containsAttacker = true;
for (Player opponent: ai.getOpponents()) {
// test whether the human can kill the ai next turn
Combat combat = new Combat(opponent);
boolean containsAttacker = false;
for (Card att : opponent.getCreaturesInPlay()) {
if (ComputerUtilCombat.canAttackNextTurn(att, ai)) {
combat.addAttacker(att, ai);
containsAttacker = true;
}
}
if (!containsAttacker) {
return false;
}
AiBlockController block = new AiBlockController(ai);
block.assignBlockersForCombat(combat);
// TODO predict other, noncombat sources of damage and add them to the "payment" variable.
// examples : Black Vise, The Rack, known direct damage spells in enemy hand, etc
// If added, might need a parameter to define whether we want to check all threats or combat threats.
if ((serious) && (ComputerUtilCombat.lifeInSeriousDanger(ai, combat, payment))) {
return true;
}
if ((!serious) && (ComputerUtilCombat.lifeInDanger(ai, combat, payment))) {
return true;
}
}
if (!containsAttacker) {
return false;
}
AiBlockController block = new AiBlockController(ai);
block.assignBlockersForCombat(combat);
// TODO predict other, noncombat sources of damage and add them to the "payment" variable.
// examples : Black Vise, The Rack, known direct damage spells in enemy hand, etc
// If added, might need a parameter to define whether we want to check all threats or combat threats.
if ((serious) && (ComputerUtilCombat.lifeInSeriousDanger(ai, combat, payment))) {
return true;
}
if ((!serious) && (ComputerUtilCombat.lifeInDanger(ai, combat, payment))) {
return true;
}
return false;
}
}

View File

@@ -171,8 +171,12 @@ public class ComputerUtilAbility {
return sa;
}
public static Card getAbilitySource(SpellAbility sa) {
return sa.getOriginalHost() != null ? sa.getOriginalHost() : sa.getHostCard();
}
public static String getAbilitySourceName(SpellAbility sa) {
final Card c = sa.getOriginalOrHost();
final Card c = getAbilitySource(sa);
return c != null ? c.getName() : "";
}

View File

@@ -47,6 +47,7 @@ import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.cost.Cost;
import forge.game.cost.CostPayEnergy;
import forge.game.cost.CostRemoveCounter;
import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordCollection;
import forge.game.keyword.KeywordInterface;
@@ -76,7 +77,7 @@ public class ComputerUtilCard {
}
});
}
return ComputerUtilCard.getMostExpensivePermanentAI(all);
return getMostExpensivePermanentAI(all);
}
/**
@@ -137,6 +138,56 @@ public class ComputerUtilCard {
return Aggregates.itemWithMin(all, CardPredicates.Accessors.fnGetCmc);
}
public static Card getBestPlaneswalkerToDamage(final List<Card> pws) {
Card bestTgt = null;
// 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(CounterEnumType.LOYALTY);
int pwScore = curLoyalty * 10;
for (SpellAbility sa : pw.getSpellAbilities()) {
if (sa.hasParam("Ultimate")) {
Integer loyaltyCost = 0;
CostRemoveCounter remLoyalty = sa.getPayCosts().getCostPartByType(CostRemoveCounter.class);
if (remLoyalty != null) {
// if remLoyalty is null, generally there's an AddCounter<0/LOYALTY> cost, like for Gideon Jura.
loyaltyCost = remLoyalty.convertAmount();
}
if (loyaltyCost != null && loyaltyCost != 0 && loyaltyCost - curLoyalty <= 1) {
// Will ultimate soon
pwScore += 10000;
}
if (pwScore > bestScore) {
bestScore = pwScore;
bestTgt = pw;
}
}
}
}
return bestTgt;
}
public static 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;
}
// The AI doesn't really pick the best enchantment, just the most expensive.
/**
* <p>
@@ -217,6 +268,63 @@ public class ComputerUtilCard {
return Aggregates.random(bLand); // random tapped land of least represented type
}
/**
* <p>
* getWorstLand.
* </p>
*
* @param lands
* @return a {@link forge.game.card.Card} object.
*/
public static Card getWorstLand(final List<Card> lands) {
Card worstLand = null;
int maxScore = Integer.MIN_VALUE;
// first, check for tapped, basic lands
for (Card tmp : lands) {
int score = tmp.isTapped() ? 2 : 0;
score += tmp.isBasicLand() ? 1 : 0;
score -= tmp.isCreature() ? 4 : 0;
for (Card aura : tmp.getEnchantedBy()) {
if (aura.getController().isOpponentOf(tmp.getController())) {
score += 5;
} else {
score -= 5;
}
}
if (score == maxScore &&
CardLists.count(lands, CardPredicates.sharesNameWith(tmp)) > CardLists.count(lands, CardPredicates.sharesNameWith(worstLand))) {
worstLand = tmp;
}
if (score > maxScore) {
worstLand = tmp;
maxScore = score;
}
}
return worstLand;
}
public static Card getBestLandToAnimate(final Iterable<Card> lands) {
Card land = null;
int maxScore = Integer.MIN_VALUE;
// first, check for tapped, basic lands
for (Card tmp : lands) {
int score = tmp.isTapped() ? 0 : 2;
score += tmp.isBasicLand() ? 2 : 0;
score -= tmp.isCreature() ? 4 : 0;
score -= 5 * tmp.getEnchantedBy().size();
if (score == maxScore &&
CardLists.count(lands, CardPredicates.sharesNameWith(tmp)) > CardLists.count(lands, CardPredicates.sharesNameWith(land))) {
land = tmp;
}
if (score > maxScore) {
land = tmp;
maxScore = score;
}
}
return land;
}
/**
* <p>
* getCheapestPermanentAI.
@@ -265,16 +373,14 @@ public class ComputerUtilCard {
* @return a {@link forge.game.card.Card} object.
*/
public static Card getBestAI(final Iterable<Card> list) {
// Get Best will filter by appropriate getBest list if ALL of the list
// is of that type
// Get Best will filter by appropriate getBest list if ALL of the list is of that type
if (Iterables.all(list, CardPredicates.Presets.CREATURES)) {
return ComputerUtilCard.getBestCreatureAI(list);
}
if (Iterables.all(list, CardPredicates.Presets.LANDS)) {
return getBestLandAI(list);
}
// TODO - Once we get an EvaluatePermanent this should call
// getBestPermanent()
// TODO - Once we get an EvaluatePermanent this should call getBestPermanent()
return ComputerUtilCard.getMostExpensivePermanentAI(list);
}
@@ -383,7 +489,7 @@ public class ComputerUtilCard {
}
if (biasLand && Iterables.any(list, CardPredicates.Presets.LANDS)) {
return ComputerUtilCard.getWorstLand(CardLists.filter(list, CardPredicates.Presets.LANDS));
return getWorstLand(CardLists.filter(list, CardPredicates.Presets.LANDS));
}
final boolean hasCreatures = Iterables.any(list, CardPredicates.Presets.CREATURES);
@@ -393,7 +499,7 @@ public class ComputerUtilCard {
List<Card> lands = CardLists.filter(list, CardPredicates.Presets.LANDS);
if (lands.size() > 6) {
return ComputerUtilCard.getWorstLand(lands);
return getWorstLand(lands);
}
if (hasEnchantmants || hasArtifacts) {
@@ -410,8 +516,7 @@ public class ComputerUtilCard {
return getWorstCreatureAI(CardLists.filter(list, CardPredicates.Presets.CREATURES));
}
// Planeswalkers fall through to here, lands will fall through if there
// aren't very many
// Planeswalkers fall through to here, lands will fall through if there aren't very many
return getCheapestPermanentAI(list, null, false);
}
@@ -444,7 +549,7 @@ public class ComputerUtilCard {
public static final Comparator<Card> EvaluateCreatureComparator = new Comparator<Card>() {
@Override
public int compare(final Card a, final Card b) {
return ComputerUtilCard.evaluateCreature(b) - ComputerUtilCard.evaluateCreature(a);
return evaluateCreature(b) - evaluateCreature(a);
}
};
@@ -550,7 +655,7 @@ public class ComputerUtilCard {
*/
public static CardCollectionView getLikelyBlockers(final Player ai, final CardCollectionView blockers) {
AiBlockController aiBlk = new AiBlockController(ai);
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
Combat combat = new Combat(opp);
//Use actual attackers if available, else consider all possible attackers
Combat currentCombat = ai.getGame().getCombat();
@@ -828,69 +933,19 @@ public class ComputerUtilCard {
return result;
}
/**
* <p>
* getWorstLand.
* </p>
*
* @param lands
* @return a {@link forge.game.card.Card} object.
*/
public static Card getWorstLand(final List<Card> lands) {
Card worstLand = null;
int maxScore = Integer.MIN_VALUE;
// first, check for tapped, basic lands
for (Card tmp : lands) {
int score = tmp.isTapped() ? 2 : 0;
score += tmp.isBasicLand() ? 1 : 0;
score -= tmp.isCreature() ? 4 : 0;
for (Card aura : tmp.getEnchantedBy()) {
if (aura.getController().isOpponentOf(tmp.getController())) {
score += 5;
} else {
score -= 5;
}
}
if (score >= maxScore) {
worstLand = tmp;
maxScore = score;
}
}
return worstLand;
} // end getWorstLand
public static Card getBestLandToAnimate(final Iterable<Card> lands) {
Card land = null;
int maxScore = Integer.MIN_VALUE;
// first, check for tapped, basic lands
for (Card tmp : lands) {
// TODO Improve this by choosing basic lands that I have plenty of mana in
int score = tmp.isTapped() ? 0 : 2;
score += tmp.isBasicLand() ? 2 : 0;
score -= tmp.isCreature() ? 4 : 0;
score -= 5 * tmp.getEnchantedBy().size();
if (score >= maxScore) {
land = tmp;
maxScore = score;
}
}
return land;
} // end getBestLandToAnimate
public static final Predicate<Deck> AI_KNOWS_HOW_TO_PLAY_ALL_CARDS = new Predicate<Deck>() {
@Override
public boolean apply(Deck d) {
for(Entry<DeckSection, CardPool> cp: d) {
for(Entry<PaperCard, Integer> e : cp.getValue()) {
if ( e.getKey().getRules().getAiHints().getRemAIDecks() )
for (Entry<DeckSection, CardPool> cp: d) {
for (Entry<PaperCard, Integer> e : cp.getValue()) {
if (e.getKey().getRules().getAiHints().getRemAIDecks())
return false;
}
}
return true;
}
};
public static List<String> chooseColor(SpellAbility sa, int min, int max, List<String> colorChoices) {
List<String> chosen = new ArrayList<>();
Player ai = sa.getActivatingPlayer();
@@ -1198,7 +1253,7 @@ public class ComputerUtilCard {
}
String kws = params.get("AddKeyword");
if (kws != null) {
bonusPT += 4 * (1 + StringUtils.countMatches(kws, "&")); //treat each added keyword as a +2/+2 for now
bonusPT += 4 * (1 + StringUtils.countMatches(kws, "&")); //treat each added keyword as a +2/+2 for now
}
if (bonusPT > 0) {
threat = bonusPT * (1 + opp.getCreaturesInPlay().size()) / 10.0f;
@@ -1212,7 +1267,7 @@ public class ComputerUtilCard {
}
final float valueNow = Math.max(valueTempo, threat);
if (valueNow < 0.2) { //hard floor to reduce ridiculous odds for instants over time
if (valueNow < 0.2) { //hard floor to reduce ridiculous odds for instants over time
return false;
}
final float chance = MyRandom.getRandom().nextFloat();
@@ -1310,7 +1365,7 @@ public class ComputerUtilCard {
threat *= 2;
}
if (c.getNetPower() == 0 && c == sa.getHostCard() && power > 0 ) {
threat *= 4; //over-value self +attack for 0 power creatures which may be pumped further after attacking
threat *= 4; //over-value self +attack for 0 power creatures which may be pumped further after attacking
}
chance += threat;
@@ -1595,7 +1650,7 @@ public class ComputerUtilCard {
*/
public static Card getPumpedCreature(final Player ai, final SpellAbility sa,
final Card c, int toughness, int power, final List<String> keywords) {
Card pumped = CardFactory.copyCard(c, true);
Card pumped = CardFactory.copyCard(c, false);
pumped.setSickness(c.hasSickness());
final long timestamp = c.getGame().getNextTimestamp();
final List<String> kws = new ArrayList<>();
@@ -1633,7 +1688,7 @@ public class ComputerUtilCard {
pumped.addChangedCardKeywords(kws, null, false, false, timestamp);
Set<CounterType> types = c.getCounters().keySet();
for(CounterType ct : types) {
pumped.addCounterFireNoEvents(ct, c.getCounters(ct), ai, true, null);
pumped.addCounterFireNoEvents(ct, c.getCounters(ct), ai, sa, true, null);
}
//Copies tap-state and extra keywords (auras, equipment, etc.)
if (c.isTapped()) {
@@ -1644,11 +1699,12 @@ public class ComputerUtilCard {
copiedKeywords.insertAll(pumped.getKeywords());
List<KeywordInterface> toCopy = Lists.newArrayList();
for (KeywordInterface k : c.getKeywords()) {
if (!copiedKeywords.contains(k.getOriginal())) {
if (k.getHidden()) {
pumped.addHiddenExtrinsicKeyword(k);
KeywordInterface copiedKI = k.copy(c, true);
if (!copiedKeywords.contains(copiedKI.getOriginal())) {
if (copiedKI.getHidden()) {
pumped.addHiddenExtrinsicKeyword(copiedKI);
} else {
toCopy.add(k);
toCopy.add(copiedKI);
}
}
}
@@ -1678,37 +1734,27 @@ public class ComputerUtilCard {
// remove old boost that might be copied
for (final StaticAbility stAb : c.getStaticAbilities()) {
vCard.removePTBoost(c.getTimestamp(), stAb.getId());
final Map<String, String> params = stAb.getMapParams();
if (!params.get("Mode").equals("Continuous")) {
if (!stAb.getParam("Mode").equals("Continuous")) {
continue;
}
if (!params.containsKey("Affected")) {
if (!stAb.hasParam("Affected")) {
continue;
}
if (!params.containsKey("AddPower") && !params.containsKey("AddToughness")) {
if (!stAb.hasParam("AddPower") && !stAb.hasParam("AddToughness")) {
continue;
}
final String valid = params.get("Affected");
if (!vCard.isValid(valid, c.getController(), c, null)) {
if (!vCard.isValid(stAb.getParam("Affected").split(","), c.getController(), c, stAb)) {
continue;
}
int att = 0;
if (params.containsKey("AddPower")) {
String addP = params.get("AddPower");
if (addP.equals("AffectedX")) {
att = CardFactoryUtil.xCount(vCard, AbilityUtils.getSVar(stAb, addP));
} else {
att = AbilityUtils.calculateAmount(c, addP, stAb);
}
if (stAb.hasParam("AddPower")) {
String addP = stAb.getParam("AddPower");
att = AbilityUtils.calculateAmount(addP.startsWith("Affected") ? vCard : c, addP, stAb, true);
}
int def = 0;
if (params.containsKey("AddToughness")) {
String addT = params.get("AddToughness");
if (addT.equals("AffectedY")) {
def = CardFactoryUtil.xCount(vCard, AbilityUtils.getSVar(stAb, addT));
} else {
def = AbilityUtils.calculateAmount(c, addT, stAb);
}
if (stAb.hasParam("AddToughness")) {
String addT = stAb.getParam("AddToughness");
def = AbilityUtils.calculateAmount(addT.startsWith("Affected") ? vCard : c, addT, stAb, true);
}
vCard.addPTBoost(att, def, c.getTimestamp(), stAb.getId());
}

View File

@@ -33,7 +33,6 @@ import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardUtil;
@@ -97,34 +96,39 @@ public class ComputerUtilCombat {
* canAttackNextTurn.
* </p>
*
* @param atacker
* @param attacker
* a {@link forge.game.card.Card} object.
* @param defender
* the defending {@link GameEntity}.
* @return a boolean.
*/
public static boolean canAttackNextTurn(final Card atacker, final GameEntity defender) {
if (!atacker.isCreature()) {
public static boolean canAttackNextTurn(final Card attacker, final GameEntity defender) {
if (!attacker.isCreature()) {
return false;
}
if (!CombatUtil.canAttackNextTurn(atacker, defender)) {
if (!CombatUtil.canAttackNextTurn(attacker, defender)) {
return false;
}
for (final KeywordInterface inst : atacker.getKeywords()) {
for (final KeywordInterface inst : attacker.getKeywords()) {
final String keyword = inst.getOriginal();
if (keyword.startsWith("CARDNAME attacks specific player each combat if able")) {
final String defined = keyword.split(":")[1];
final Player player = AbilityUtils.getDefinedPlayers(atacker, defined, null).get(0);
final Player player = AbilityUtils.getDefinedPlayers(attacker, defined, null).get(0);
if (!defender.equals(player)) {
return false;
}
}
}
// TODO this should be a factor but needs some alignment with AttachAi
//boolean leavesPlay = !ComputerUtilCard.hasActiveUndyingOrPersist(attacker)
// && ((attacker.hasKeyword(Keyword.VANISHING) && attacker.getCounters(CounterEnumType.TIME) == 1)
// || (attacker.hasKeyword(Keyword.FADING) && attacker.getCounters(CounterEnumType.FADE) == 0)
// || attacker.hasSVar("EndOfTurnLeavePlay"));
// The creature won't untap next turn
return !atacker.isTapped() || Untap.canUntap(atacker);
} // canAttackNextTurn(Card, GameEntity)
return !attacker.isTapped() || Untap.canUntap(attacker);
}
/**
* <p>
@@ -330,7 +334,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
// ai can't get poison counters, so the value can't change
if (!ai.canReceiveCounters(CounterEnumType.POISON)) {
return ai.getPoisonCounters();
}
@@ -408,7 +412,6 @@ public class ComputerUtilCombat {
return false;
}
// check for creatures that must be blocked
final List<Card> attackers = combat.getAttackersOf(ai);
@@ -469,7 +472,6 @@ public class ComputerUtilCombat {
* @return a boolean.
*/
public static boolean wouldLoseLife(final Player ai, final Combat combat) {
return (ComputerUtilCombat.lifeThatWouldRemain(ai, combat) < ai.getLife());
}
@@ -488,8 +490,7 @@ public class ComputerUtilCombat {
}
public static boolean lifeInSeriousDanger(final Player ai, final Combat combat, final int payment) {
// life in danger only cares about the player's life. Not about a
// Planeswalkers life
// life in danger only cares about the player's life. Not about a Planeswalkers life
if (ai.cantLose() || combat == null) {
return false;
}
@@ -564,8 +565,7 @@ public class ComputerUtilCombat {
return damage;
}
// This calculates the amount of damage a blocker in a blockgang can deal to
// the attacker
// This calculates the amount of damage a blocker in a blockgang can deal to the attacker
/**
* <p>
* dealsDamageAsBlocker.
@@ -578,7 +578,6 @@ public class ComputerUtilCombat {
* @return a int.
*/
public static int dealsDamageAsBlocker(final Card attacker, final Card defender) {
int defenderDamage = predictDamageByBlockerWithoutDoubleStrike(attacker, defender);
if (defender.hasKeyword(Keyword.DOUBLE_STRIKE)) {
@@ -643,7 +642,6 @@ public class ComputerUtilCombat {
* @return a int.
*/
public static int totalShieldDamage(final Card attacker, final List<Card> defenders) {
int defenderDefense = 0;
for (final Card defender : defenders) {
@@ -667,7 +665,6 @@ public class ComputerUtilCombat {
* @return a int.
*/
public static int shieldDamage(final Card attacker, final Card blocker) {
if (ComputerUtilCombat.canDestroyBlockerBeforeFirstStrike(blocker, attacker, false)) {
return 0;
}
@@ -772,7 +769,6 @@ public class ComputerUtilCombat {
public static boolean combatTriggerWillTrigger(final Card attacker, final Card defender, final Trigger trigger,
Combat combat, final List<Card> plannedAttackers) {
final Game game = attacker.getGame();
final Map<String, String> trigParams = trigger.getMapParams();
boolean willTrigger = false;
final Card source = trigger.getHostCard();
if (combat == null) {
@@ -795,29 +791,27 @@ public class ComputerUtilCombat {
if (combat.isAttacking(attacker)) {
return false; // The trigger should have triggered already
}
if (trigParams.containsKey("ValidCard")) {
if (!trigger.matchesValid(attacker, trigParams.get("ValidCard").split(","))
&& !(combat.isAttacking(source) && trigger.matchesValid(source,
trigParams.get("ValidCard").split(","))
&& !trigParams.containsKey("Alone"))) {
if (trigger.hasParam("ValidCard")) {
if (!trigger.matchesValidParam("ValidCard", attacker)
&& !(combat.isAttacking(source) && trigger.matchesValidParam("ValidCard", source)
&& !trigger.hasParam("Alone"))) {
return false;
}
}
if (trigParams.containsKey("Attacked")) {
if (combat.isAttacking(attacker)) {
GameEntity attacked = combat.getDefenderByAttacker(attacker);
if (!trigger.matchesValid(attacked, trigParams.get("Attacked").split(","))) {
return false;
}
} else {
if ("You,Planeswalker.YouCtrl".equals(trigParams.get("Attacked"))) {
if (source.getController() == attacker.getController()) {
return false;
}
}
}
if (trigger.hasParam("Attacked")) {
if (combat.isAttacking(attacker)) {
if (!trigger.matchesValidParam("Attacked", combat.getDefenderByAttacker(attacker))) {
return false;
}
} else {
if ("You,Planeswalker.YouCtrl".equals(trigger.getParam("Attacked"))) {
if (source.getController() == attacker.getController()) {
return false;
}
}
}
}
if (trigParams.containsKey("Alone") && plannedAttackers != null && plannedAttackers.size() != 1) {
if (trigger.hasParam("Alone") && plannedAttackers != null && plannedAttackers.size() != 1) {
return false; // won't trigger since the AI is planning to attack with more than one creature
}
}
@@ -825,10 +819,8 @@ public class ComputerUtilCombat {
// defender == null means unblocked
if ((defender == null) && mode == TriggerType.AttackerUnblocked) {
willTrigger = true;
if (trigParams.containsKey("ValidCard")) {
if (!trigger.matchesValid(attacker, trigParams.get("ValidCard").split(","))) {
return false;
}
if (!trigger.matchesValidParam("ValidCard", attacker)) {
return false;
}
}
@@ -838,8 +830,8 @@ public class ComputerUtilCombat {
if (mode == TriggerType.Blocks) {
willTrigger = true;
if (trigParams.containsKey("ValidBlocked")) {
String validBlocked = trigParams.get("ValidBlocked");
if (trigger.hasParam("ValidBlocked")) {
String validBlocked = trigger.getParam("ValidBlocked");
if (validBlocked.contains(".withLesserPower")) {
// Have to check this restriction here as triggering objects aren't set yet, so
// ValidBlocked$Creature.powerLTX where X:TriggeredBlocker$CardPower crashes with NPE
@@ -852,8 +844,8 @@ public class ComputerUtilCombat {
return false;
}
}
if (trigParams.containsKey("ValidCard")) {
String validBlocker = trigParams.get("ValidCard");
if (trigger.hasParam("ValidCard")) {
String validBlocker = trigger.getParam("ValidCard");
if (validBlocker.contains(".withLesserPower")) {
// Have to check this restriction here as triggering objects aren't set yet, so
// ValidCard$Creature.powerLTX where X:TriggeredAttacker$CardPower crashes with NPE
@@ -868,29 +860,23 @@ public class ComputerUtilCombat {
}
} else if (mode == TriggerType.AttackerBlocked || mode == TriggerType.AttackerBlockedByCreature) {
willTrigger = true;
if (trigParams.containsKey("ValidBlocker")) {
if (!trigger.matchesValid(defender, trigParams.get("ValidBlocker").split(","))) {
return false;
}
if (!trigger.matchesValidParam("ValidBlocker", defender)) {
return false;
}
if (trigParams.containsKey("ValidCard")) {
if (!trigger.matchesValid(attacker, trigParams.get("ValidCard").split(","))) {
return false;
}
if (!trigger.matchesValidParam("ValidCard", attacker)) {
return false;
}
} else if (mode == TriggerType.DamageDone) {
willTrigger = true;
if (trigParams.containsKey("ValidSource")) {
if (!(trigger.matchesValid(defender, trigParams.get("ValidSource").split(","))
if (trigger.hasParam("ValidSource")) {
if (!(trigger.matchesValidParam("ValidSource", defender)
&& defender.getNetCombatDamage() > 0
&& (!trigParams.containsKey("ValidTarget")
|| trigger.matchesValid(attacker, trigParams.get("ValidTarget").split(","))))) {
&& trigger.matchesValidParam("ValidTarget", attacker))) {
return false;
}
if (!(trigger.matchesValid(attacker, trigParams.get("ValidSource").split(","))
if (!(trigger.matchesValidParam("ValidSource", attacker)
&& attacker.getNetCombatDamage() > 0
&& (!trigParams.containsKey("ValidTarget")
|| trigger.matchesValid(defender, trigParams.get("ValidTarget").split(","))))) {
&& trigger.matchesValidParam("ValidTarget", defender))) {
return false;
}
}
@@ -939,30 +925,22 @@ public class ComputerUtilCombat {
}
final Game game = attacker.getGame();
// look out for continuous static abilities that only care for blocking
// creatures
// look out for continuous static abilities that only care for blocking creatures
final CardCollectionView cardList = CardCollection.combine(game.getCardsIn(ZoneType.Battlefield), game.getCardsIn(ZoneType.Command));
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 (!stAb.getParam("Mode").equals("Continuous")) {
continue;
}
if (!params.containsKey("Affected") || !params.get("Affected").contains("blocking")) {
if (!stAb.hasParam("Affected") || !stAb.getParam("Affected").contains("blocking")) {
continue;
}
final String valid = TextUtil.fastReplace(params.get("Affected"), "blocking", "Creature");
if (!blocker.isValid(valid, card.getController(), card, null)) {
final String valid = TextUtil.fastReplace(stAb.getParam("Affected"), "blocking", "Creature");
if (!blocker.isValid(valid, card.getController(), card, stAb)) {
continue;
}
if (params.containsKey("AddPower")) {
if (params.get("AddPower").equals("X")) {
power += CardFactoryUtil.xCount(card, card.getSVar("X"));
} else if (params.get("AddPower").equals("Y")) {
power += CardFactoryUtil.xCount(card, card.getSVar("Y"));
} else {
power += Integer.valueOf(params.get("AddPower"));
}
if (stAb.hasParam("AddPower")) {
power += AbilityUtils.calculateAmount(card, stAb.getParam("AddPower"), stAb);
}
}
}
@@ -1247,31 +1225,23 @@ public class ComputerUtilCombat {
theTriggers.addAll(blocker.getTriggers());
}
// look out for continuous static abilities that only care for attacking
// creatures
// look out for continuous static abilities that only care for attacking creatures
if (!withoutCombatStaticAbilities) {
final CardCollectionView cardList = CardCollection.combine(game.getCardsIn(ZoneType.Battlefield), game.getCardsIn(ZoneType.Command));
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 (!stAb.getParam("Mode").equals("Continuous")) {
continue;
}
if (!params.containsKey("Affected") || !params.get("Affected").contains("attacking")) {
if (!stAb.hasParam("Affected") || !stAb.getParam("Affected").contains("attacking")) {
continue;
}
final String valid = TextUtil.fastReplace(params.get("Affected"), "attacking", "Creature");
if (!attacker.isValid(valid, card.getController(), card, null)) {
final String valid = TextUtil.fastReplace(stAb.getParam("Affected"), "attacking", "Creature");
if (!attacker.isValid(valid, card.getController(), card, stAb)) {
continue;
}
if (params.containsKey("AddPower")) {
if (params.get("AddPower").equals("X")) {
power += CardFactoryUtil.xCount(card, card.getSVar("X"));
} else if (params.get("AddPower").equals("Y")) {
power += CardFactoryUtil.xCount(card, card.getSVar("Y"));
} else {
power += Integer.valueOf(params.get("AddPower"));
}
if (stAb.hasParam("AddPower")) {
power += AbilityUtils.calculateAmount(card, stAb.getParam("AddPower"), stAb);
}
}
}
@@ -1345,7 +1315,7 @@ public class ComputerUtilCombat {
} else if (bonus.contains("TriggeredAttacker$CardToughness")) {
bonus = TextUtil.fastReplace(bonus, "TriggeredAttacker$CardToughness", TextUtil.concatNoSpace("Number$", String.valueOf(attacker.getNetToughness())));
}
power += CardFactoryUtil.xCount(source, bonus);
power += AbilityUtils.calculateAmount(source, bonus, sa);
}
}
@@ -1442,8 +1412,7 @@ public class ComputerUtilCombat {
theTriggers.addAll(blocker.getTriggers());
}
// look out for continuous static abilities that only care for attacking
// creatures
// look out for continuous static abilities that only care for attacking creatures
if (!withoutCombatStaticAbilities) {
final CardCollectionView cardList = game.getCardsIn(ZoneType.Battlefield);
for (final Card card : cardList) {
@@ -1535,7 +1504,7 @@ public class ComputerUtilCombat {
} else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee
bonus = TextUtil.fastReplace(bonus, "TriggeredPlayersDefenders$Amount", "Number$1");
}
toughness += CardFactoryUtil.xCount(source, bonus);
toughness += AbilityUtils.calculateAmount(source, bonus, sa);
}
} else if (ApiType.PumpAll.equals(sa.getApi())) {
@@ -1568,7 +1537,7 @@ public class ComputerUtilCombat {
} else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee
bonus = TextUtil.fastReplace(bonus, "TriggeredPlayersDefenders$Amount", "Number$1");
}
toughness += CardFactoryUtil.xCount(source, bonus);
toughness += AbilityUtils.calculateAmount(source, bonus, sa);
}
}
}
@@ -1633,8 +1602,8 @@ public class ComputerUtilCombat {
//Check triggers that deal damage or shrink the attacker
if (ComputerUtilCombat.getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities) <= 0) {
return true;
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, withoutAbilities) <= 0) {
return true;
}
// check Destroy triggers (Cockatrice and friends)
@@ -2251,11 +2220,12 @@ public class ComputerUtilCombat {
* @return a int.
*/
public final static int getDamageToKill(final Card c) {
int killDamage = c.getLethalDamage() + c.getPreventNextDamageTotalShields();
int damageShield = c.getPreventNextDamageTotalShields();
int killDamage = c.getLethalDamage() + damageShield;
if ((killDamage > c.getPreventNextDamageTotalShields())
if ((killDamage > damageShield)
&& c.hasSVar("DestroyWhenDamaged")) {
killDamage = 1 + c.getPreventNextDamageTotalShields();
killDamage = 1 + damageShield;
}
return killDamage;
@@ -2277,7 +2247,6 @@ public class ComputerUtilCombat {
*/
public final static int predictDamageTo(final Player target, final int damage, final Card source, final boolean isCombat) {
final Game game = target.getGame();
int restDamage = damage;
@@ -2286,39 +2255,38 @@ public class ComputerUtilCombat {
// Predict replacement effects
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")) {
if (!re.getMode().equals(ReplacementType.DamageDone) ||
(!re.hasParam("PreventionEffect") && !re.hasParam("Prevent"))) {
continue;
}
// Immortal Coil prevents the damage but has a similar negative effect
if ("Immortal Coil".equals(ca.getName())) {
continue;
}
if (params.containsKey("ValidSource")
&& !source.isValid(params.get("ValidSource"), ca.getController(), ca, null)) {
if (!re.matchesValidParam("ValidSource", source)) {
continue;
}
if (params.containsKey("ValidTarget")
&& !target.isValid(params.get("ValidTarget"), ca.getController(), ca, null)) {
if (!re.matchesValidParam("ValidTarget", source)) {
continue;
}
if (params.containsKey("IsCombat")) {
if (params.get("IsCombat").equals("True")) {
if (!isCombat) {
continue;
}
} else {
if (isCombat) {
continue;
}
if (re.hasParam("IsCombat")) {
if (re.getParam("IsCombat").equals("True") != isCombat) {
continue;
}
}
if (re.hasParam("Prevent")) {
return 0;
} else if (re.getOverridingAbility() != null) {
SpellAbility repSA = re.getOverridingAbility();
if (repSA.getApi() == ApiType.ReplaceDamage) {
return Math.max(0, restDamage - AbilityUtils.calculateAmount(ca, repSA.getParam("Amount"), repSA));
}
}
return 0;
}
}
restDamage = target.staticDamagePrevention(restDamage, source, isCombat, true);
restDamage = target.staticDamagePrevention(restDamage, 0, source, isCombat);
return restDamage;
}
@@ -2339,13 +2307,7 @@ public class ComputerUtilCombat {
// This function helps the AI calculate the actual amount of damage an
// effect would deal
public final static int predictDamageTo(final Card target, final int damage, final Card source, final boolean isCombat) {
int restDamage = damage;
restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
restDamage = target.staticDamagePrevention(restDamage, source, isCombat, true);
return restDamage;
return predictDamageTo(target, damage, 0, source, isCombat);
}
@@ -2367,7 +2329,6 @@ public class ComputerUtilCombat {
* @return a int.
*/
public final static int predictDamageTo(final Card target, final int damage, final int possiblePrevention, final Card source, final boolean isCombat) {
int restDamage = damage;
restDamage = target.staticReplaceDamage(restDamage, source, isCombat);
@@ -2377,7 +2338,6 @@ public class ComputerUtilCombat {
}
public final static boolean dealsFirstStrikeDamage(final Card combatant, final boolean withoutAbilities, final Combat combat) {
if (combatant.hasKeyword(Keyword.DOUBLE_STRIKE) || combatant.hasKeyword(Keyword.FIRST_STRIKE)) {
return true;
}
@@ -2485,7 +2445,14 @@ public class ComputerUtilCombat {
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(
ReplacementType.DamageDone, repParams, ReplacementLayer.Other);
return !list.isEmpty();
for (final ReplacementEffect re : list) {
Map<String, String> params = re.getMapParams();
if (params.containsKey("Prevent") ||
(re.getOverridingAbility() != null && re.getOverridingAbility().getApi() != ApiType.ReplaceDamage && re.getOverridingAbility().getApi() != ApiType.ReplaceEffect)) {
return true;
}
}
return false;
}
public static boolean attackerHasThreateningAfflict(Card attacker, Player aiDefender) {
@@ -2494,20 +2461,6 @@ public class ComputerUtilCombat {
return afflictDmg > attacker.getNetPower() || afflictDmg >= aiDefender.getLife();
}
public static int getMaxAttackersFor(final GameEntity defender) {
if (defender instanceof Player) {
for (final Card card : ((Player) defender).getCardsIn(ZoneType.Battlefield)) {
if (card.hasKeyword("No more than one creature can attack you each combat.")) {
return 1;
} else if (card.hasKeyword("No more than two creatures can attack you each combat.")) {
return 2;
}
}
}
return -1;
}
public static List<Card> categorizeAttackersByEvasion(List<Card> attackers) {
List<Card> categorizedAttackers = Lists.newArrayList();
@@ -2597,5 +2550,3 @@ public class ComputerUtilCombat {
return false;
}
}

View File

@@ -3,10 +3,13 @@ package forge.ai;
import java.util.List;
import java.util.Set;
import forge.game.ability.ApiType;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -19,6 +22,7 @@ import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CardUtil;
import forge.game.card.CounterEnumType;
@@ -137,9 +141,8 @@ public class ComputerUtilCost {
* the source
* @return true, if successful
*/
public static boolean checkDiscardCost(final Player ai, final Cost cost, final Card source, SpellAbility sa) {
if (cost == null || source.hasSVar("AISkipDiscardCostCheck") /* FIXME: should not be needed! */ ) {
if (cost == null) {
return true;
}
@@ -157,7 +160,7 @@ public class ComputerUtilCost {
if (typeList.size() > ai.getMaxHandSize()) {
continue;
}
int num = AbilityUtils.calculateAmount(source, disc.getAmount(), null);
int num = AbilityUtils.calculateAmount(source, disc.getAmount(), sa);
for (int i = 0; i < num; i++) {
Card pref = ComputerUtil.getCardPreference(ai, source, "DiscardCost", typeList);
@@ -270,7 +273,10 @@ public class ComputerUtilCost {
}
final CardCollection sacList = new CardCollection();
final CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sourceAbility);
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sourceAbility);
// don't sacrifice the card we're pumping
typeList = paymentChoicesWithoutTargets(typeList, sourceAbility, ai);
int count = 0;
while (count < amount) {
@@ -320,11 +326,14 @@ public class ComputerUtilCost {
}
final CardCollection sacList = new CardCollection();
final CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sourceAbility);
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sourceAbility);
// don't sacrifice the card we're pumping
typeList = paymentChoicesWithoutTargets(typeList, sourceAbility, ai);
int count = 0;
while (count < amount) {
Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList);
Card prefCard = ComputerUtil.getCardPreference(ai, source, "SacCost", typeList, sourceAbility);
if (prefCard == null) {
return false;
}
@@ -337,18 +346,31 @@ public class ComputerUtilCost {
return true;
}
/**
* Check sacrifice cost.
*
* @param cost
* the cost
* @param source
* the source
* @return true, if successful
*/
public static boolean checkSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility) {
return checkSacrificeCost(ai, cost, source, sourceAbility, true);
}
public static boolean isSacrificeSelfCost(final Cost cost) {
if (cost == null) {
return false;
}
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostSacrifice) {
if ("CARDNAME".equals(part.getType())) {
return true;
}
}
}
return false;
if (cost == null) {
return false;
}
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostSacrifice) {
if ("CARDNAME".equals(part.getType())) {
return true;
}
}
}
return false;
}
/**
@@ -374,7 +396,7 @@ public class ComputerUtilCost {
* - break board stall by racing with evasive vehicle
*/
if (sa.hasParam("Crew")) {
Card vehicle = AnimateAi.becomeAnimated(source, sa);
Card vehicle = AnimateAi.becomeAnimated(source, sa);
final int vehicleValue = ComputerUtilCard.evaluateCreature(vehicle);
String type = part.getType();
String totalP = type.split("withTotalPowerGE")[1];
@@ -391,25 +413,12 @@ public class ComputerUtilCost {
return ComputerUtil.chooseTapTypeAccumulatePower(ai, type, sa, true,
Integer.parseInt(totalP), exclude) != null;
}
return false;
return false;
}
}
return true;
}
/**
* Check sacrifice cost.
*
* @param cost
* the cost
* @param source
* the source
* @return true, if successful
*/
public static boolean checkSacrificeCost(final Player ai, final Cost cost, final Card source, final SpellAbility sourceAbility) {
return checkSacrificeCost(ai, cost, source, sourceAbility,true);
}
/**
* <p>
* shouldPayCost.
@@ -420,8 +429,8 @@ public class ComputerUtilCost {
* @param cost
* @return a boolean.
*/
@Deprecated
public static boolean shouldPayCost(final Player ai, final Card hostCard, final Cost cost) {
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostPayLife) {
if (!ai.cantLoseForZeroOrLessLife()) {
@@ -479,9 +488,9 @@ public class ComputerUtilCost {
}
}
for (Card c : player.getCardsIn(ZoneType.Command)) {
if (cannotBeCountered) {
continue;
}
if (cannotBeCountered) {
continue;
}
final String snem = c.getSVar("SpellsNeedExtraManaEffect");
if (!StringUtils.isBlank(snem)) {
if (StringUtils.isNumeric(snem)) {
@@ -549,7 +558,7 @@ public class ComputerUtilCost {
}
return ComputerUtilMana.canPayManaCost(sa, player, extraManaNeeded)
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
} // canPayCost()
public static boolean willPayUnlessCost(SpellAbility sa, Player payer, Cost cost, boolean alreadyPaid, FCollectionView<Player> payers) {
@@ -558,7 +567,6 @@ public class ComputerUtilCost {
boolean payForOwnOnly = "OnlyOwn".equals(aiLogic);
boolean payOwner = sa.hasParam("UnlessAI") && aiLogic.startsWith("Defined");
boolean payNever = "Never".equals(aiLogic);
boolean shockland = "Shockland".equals(aiLogic);
boolean isMine = sa.getActivatingPlayer().equals(payer);
if (payNever) { return false; }
@@ -573,26 +581,6 @@ public class ComputerUtilCost {
if (sa.getHostCard() == null || payer.equals(sa.getHostCard().getController())) {
return false;
}
} else if (shockland) {
if (payer.getLife() > 3 && payer.canPayLife(2)) {
final int landsize = payer.getLandsInPlay().size() + 1;
for (Card c : payer.getCardsIn(ZoneType.Hand)) {
// if the new land size would equal the CMC of a card in AIs hand, consider playing it untapped,
// otherwise don't bother running other checks
if (landsize != c.getCMC()) {
continue;
}
// try to determine in the AI is actually planning to play a spell ability from the card
boolean willPlay = ComputerUtil.hasReasonToPlayCardThisTurn(payer, c);
// try to determine if the mana shards provided by the lands would be applicable to pay the mana cost
boolean canPay = c.getManaCost().canBePaidWithAvaliable(ColorSet.fromNames(getAvailableManaColors(payer, source)).getColor());
if (canPay && willPlay) {
return true;
}
}
}
return false;
} else if ("Paralyze".equals(aiLogic)) {
final Card c = source.getEnchantingCard();
if (c == null || c.isUntapped()) {
@@ -610,10 +598,10 @@ public class ComputerUtilCost {
return false;
}
} else if (aiLogic != null && aiLogic.startsWith("LifeLE")) {
// if payer can't lose life its no need to pay unless
if (!payer.canLoseLife())
return false;
else if (payer.getLife() <= Integer.valueOf(aiLogic.substring(6))) {
// if payer can't lose life its no need to pay unless
if (!payer.canLoseLife())
return false;
else if (payer.getLife() <= AbilityUtils.calculateAmount(source, aiLogic.substring(6), sa)) {
return true;
}
} else if ("WillAttack".equals(aiLogic)) {
@@ -630,6 +618,29 @@ public class ComputerUtilCost {
return false;
}
// Check for shocklands and similar ETB replacement effects
if (sa.hasParam("ETB") && sa.getApi().equals(ApiType.Tap)) {
for (final CostPart part : cost.getCostParts()) {
if (part instanceof CostPayLife) {
final CostPayLife lifeCost = (CostPayLife) part;
Integer amount = lifeCost.convertAmount();
if (payer.getLife() > (amount + 1) && payer.canPayLife(amount)) {
final int landsize = payer.getLandsInPlay().size() + 1;
for (Card c : payer.getCardsIn(ZoneType.Hand)) {
// Check if the AI has enough lands to play the card
if (landsize != c.getCMC()) {
continue;
}
// Check if the AI intends to play the card and if it can pay for it with the mana it has
boolean willPlay = ComputerUtil.hasReasonToPlayCardThisTurn(payer, c);
boolean canPay = c.getManaCost().canBePaidWithAvaliable(ColorSet.fromNames(getAvailableManaColors(payer, source)).getColor());
return canPay && willPlay;
}
}
}
}
}
// AI will only pay when it's not already payed and only opponents abilities
if (alreadyPaid || (payers.size() > 1 && (isMine && !payForOwnOnly))) {
return false;
@@ -639,13 +650,13 @@ public class ComputerUtilCost {
// Didn't have any of the data on the original SA to pay dependant costs
return checkLifeCost(payer, cost, source, 4, sa)
&& checkDamageCost(payer, cost, source, 4)
&& (isMine || checkSacrificeCost(payer, cost, source, sa))
&& (isMine || checkDiscardCost(payer, cost, source, sa))
&& (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2)
&& (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2)
&& (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1)
&& (!source.getName().equals("Chain of Vapor") || (payer.getWeakestOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3));
&& checkDamageCost(payer, cost, source, 4)
&& (isMine || checkSacrificeCost(payer, cost, source, sa))
&& (isMine || checkDiscardCost(payer, cost, source, sa))
&& (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2)
&& (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2)
&& (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1)
&& (!source.getName().equals("Chain of Vapor") || (payer.getWeakestOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3));
}
public static Set<String> getAvailableManaColors(Player ai, Card additionalLand) {
@@ -685,14 +696,14 @@ public class ComputerUtilCost {
public static int getMaxXValue(SpellAbility sa, Player ai) {
final Card source = sa.getHostCard();
final SpellAbility root = sa.getRootAbility();
final Cost abCost = sa.getPayCosts();
final Cost abCost = root.getPayCosts();
if (abCost == null || !abCost.hasXInAnyCostPart()) {
return 0;
}
Integer val = null;
if (sa.costHasManaX()) {
if (root.costHasManaX()) {
val = ComputerUtilMana.determineLeftoverMana(root, ai);
}
@@ -739,4 +750,12 @@ public class ComputerUtilCost {
}
return ObjectUtils.defaultIfNull(val, 0);
}
public static CardCollection paymentChoicesWithoutTargets(Iterable<Card> choices, SpellAbility source, Player ai) {
if (source.usesTargeting()) {
final CardCollection targets = new CardCollection(source.getTargets().getTargetCards());
choices = Iterables.filter(choices, Predicates.not(Predicates.and(CardPredicates.isController(ai), Predicates.in(targets))));
}
return new CardCollection(choices);
}
}

View File

@@ -1,26 +1,8 @@
package forge.ai;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.*;
import forge.ai.ability.AnimateAi;
import forge.card.ColorSet;
import forge.card.MagicColor;
@@ -34,20 +16,10 @@ import forge.game.GameActionUtil;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardUtil;
import forge.game.card.CounterEnumType;
import forge.game.card.*;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.cost.Cost;
import forge.game.cost.CostAdjustment;
import forge.game.cost.CostPartMana;
import forge.game.cost.CostPayEnergy;
import forge.game.cost.CostPayment;
import forge.game.cost.*;
import forge.game.keyword.Keyword;
import forge.game.mana.Mana;
import forge.game.mana.ManaCostBeingPaid;
@@ -67,6 +39,10 @@ import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
public class ComputerUtilMana {
private final static boolean DEBUG_MANA_PAYMENT = false;
@@ -272,28 +248,87 @@ public class ComputerUtilMana {
public static SpellAbility chooseManaAbility(ManaCostBeingPaid cost, SpellAbility sa, Player ai, ManaCostShard toPay,
Collection<SpellAbility> saList, boolean checkCosts) {
Card saHost = sa.getHostCard();
// CastTotalManaSpent (AIPreference:ManaFrom$Type or AIManaPref$ Type)
String manaSourceType = "";
if (saHost.hasSVar("AIPreference")) {
String condition = saHost.getSVar("AIPreference");
if (condition.startsWith("ManaFrom")) {
manaSourceType = TextUtil.split(condition, '$')[1];
}
} else if (sa.hasParam("AIManaPref")) {
manaSourceType = sa.getParam("AIManaPref");
}
if (manaSourceType != "") {
List<SpellAbility> filteredList = Lists.newArrayList(saList);
switch (manaSourceType) {
case "Snow":
filteredList.sort(new Comparator<SpellAbility>() {
@Override
public int compare(SpellAbility ab1, SpellAbility ab2) {
return ab1.getHostCard() != null && ab1.getHostCard().isSnow()
&& ab2.getHostCard() != null && !ab2.getHostCard().isSnow() ? -1 : 1;
}
});
saList = filteredList;
break;
case "Treasure":
// Try to spend only one Treasure if possible
filteredList.sort(new Comparator<SpellAbility>() {
@Override
public int compare(SpellAbility ab1, SpellAbility ab2) {
return ab1.getHostCard() != null && ab1.getHostCard().getType().hasSubtype("Treasure")
&& ab2.getHostCard() != null && !ab2.getHostCard().getType().hasSubtype("Treasure") ? -1 : 1;
}
});
SpellAbility first = filteredList.get(0);
if (first.getHostCard() != null && first.getHostCard().getType().hasSubtype("Treasure")) {
saList.remove(first);
List<SpellAbility> updatedList = Lists.newArrayList();
updatedList.add(first);
updatedList.addAll(saList);
saList = updatedList;
}
break;
case "TreasureMax":
// Ok to spend as many Treasures as possible
filteredList.sort(new Comparator<SpellAbility>() {
@Override
public int compare(SpellAbility ab1, SpellAbility ab2) {
return ab1.getHostCard() != null && ab1.getHostCard().getType().hasSubtype("Treasure")
&& ab2.getHostCard() != null && !ab2.getHostCard().getType().hasSubtype("Treasure") ? -1 : 1;
}
});
saList = filteredList;
break;
default:
break;
}
}
for (final SpellAbility ma : saList) {
if (ma.getHostCard() == sa.getHostCard()) {
if (ma.getHostCard() == saHost) {
continue;
}
if (sa.getHostCard() != null) {
if (saHost != null) {
if (sa.getApi() == ApiType.Animate) {
// For abilities like Genju of the Cedars, make sure that we're not activating the aura ability by tapping the enchanted card for mana
if (sa.getHostCard().isAura() && "Enchanted".equals(sa.getParam("Defined"))
&& ma.getHostCard() == sa.getHostCard().getEnchantingCard()
if (saHost.isAura() && "Enchanted".equals(sa.getParam("Defined"))
&& ma.getHostCard() == saHost.getEnchantingCard()
&& ma.getPayCosts().hasTapCost()) {
continue;
}
// If a manland was previously animated this turn, do not tap it to animate another manland
if (sa.getHostCard().isLand() && ma.getHostCard().isLand()
if (saHost.isLand() && ma.getHostCard().isLand()
&& ai.getController().isAI()
&& AnimateAi.isAnimatedThisTurn(ai, ma.getHostCard())) {
continue;
}
} else if (sa.getApi() == ApiType.Pump) {
if ((sa.getHostCard().isInstant() || sa.getHostCard().isSorcery())
if ((saHost.isInstant() || saHost.isSorcery())
&& ma.getHostCard().isCreature()
&& ai.getController().isAI()
&& ma.getPayCosts().hasTapCost()
@@ -303,7 +338,7 @@ public class ComputerUtilMana {
continue;
}
} else if (sa.getApi() == ApiType.Attach
&& "AvoidPayingWithAttachTarget".equals(sa.getHostCard().getSVar("AIPaymentPreference"))) {
&& "AvoidPayingWithAttachTarget".equals(saHost.getSVar("AIPaymentPreference"))) {
// For cards like Genju of the Cedars, make sure we're not attaching to the same land that will
// be tapped to pay its own cost if there's another untapped land like that available
if (ma.getHostCard().equals(sa.getTargetCard())) {
@@ -319,8 +354,8 @@ public class ComputerUtilMana {
// Exception: when paying generic mana with Cavern of Souls, prefer the colored mana producing ability
// to attempt to make the spell uncounterable when possible.
if (ComputerUtilAbility.getAbilitySourceName(ma).equals("Cavern of Souls") && ma.hasChosenType()
&& sa.getHostCard().getType().getCreatureTypes().contains(ma.getChosenType(0))) {
if (ComputerUtilAbility.getAbilitySourceName(ma).equals("Cavern of Souls")
&& saHost.getType().getCreatureTypes().contains(ma.getHostCard().getChosenType())) {
if (toPay == ManaCostShard.COLORLESS && cost.getUnpaidShards().contains(ManaCostShard.GENERIC)) {
// Deprioritize Cavern of Souls, try to pay generic mana with it instead to use the NoCounter ability
continue;
@@ -341,7 +376,7 @@ public class ComputerUtilMana {
continue;
}
if (canPayShardWithSpellAbility(toPay, ai, paymentChoice, sa, checkCosts)) {
if (canPayShardWithSpellAbility(toPay, ai, paymentChoice, sa, checkCosts, cost.getXManaCostPaidByColor())) {
return paymentChoice;
}
}
@@ -414,6 +449,7 @@ public class ComputerUtilMana {
// then apply this one
if (!replaceType.isEmpty()) {
for (SpellAbility saMana : replaceAmount) {
Card card = saMana.getHostCard();
if (saMana.hasParam("ReplaceType")) {
// replace color and colorless
String color = saMana.getParam("ReplaceType");
@@ -435,8 +471,8 @@ public class ComputerUtilMana {
// replace color
String color = saMana.getParam("ReplaceColor");
if ("Chosen".equals(color)) {
if (saMana.hasChosenColor()) {
color = MagicColor.toShortString(saMana.getChosenColor());
if (card.hasChosenColor()) {
color = MagicColor.toShortString(card.getChosenColor());
}
}
if (saMana.hasParam("ReplaceOnly")) {
@@ -488,7 +524,7 @@ public class ComputerUtilMana {
int pAmount = AbilityUtils.calculateAmount(trSA.getHostCard(), trSA.getParamOrDefault("Amount", "1"), trSA);
String produced = trSA.getParam("Produced");
if (produced.equals("Chosen")) {
produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor(trSA));
produced = MagicColor.toShortString(trSA.getHostCard().getChosenColor());
}
manaProduced += " " + StringUtils.repeat(produced, pAmount);
} else if (ApiType.ManaReflected.equals(trSA.getApi())) {
@@ -546,7 +582,7 @@ public class ComputerUtilMana {
}
// get a mana of this type from floating, bail if none available
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), (byte) -1);
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), (byte) -1, cost.getXManaCostPaidByColor());
if (mana != null) {
if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana, false)) {
manaSpentToPay.add(0, mana);
@@ -608,7 +644,6 @@ public class ComputerUtilMana {
String manaProduced = predictManafromSpellAbility(saPayment, ai, toPay);
//System.out.println(manaProduced);
payMultipleMana(cost, manaProduced, ai);
// remove from available lists
@@ -788,7 +823,6 @@ public class ComputerUtilMana {
String manaProduced = ignoreColor || ignoreType ? MagicColor.toShortString(toPay.getColorMask())
: predictManafromSpellAbility(saPayment, ai, toPay);
// System.out.println(manaProduced);
payMultipleMana(cost, manaProduced, ai);
// remove from available lists
@@ -935,7 +969,7 @@ public class ComputerUtilMana {
}
// get a mana of this type from floating, bail if none available
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), hasConverge ? cost.getColorsPaid() : -1);
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), hasConverge ? cost.getColorsPaid() : -1, cost.getXManaCostPaidByColor());
if (mana != null) {
if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana, test)) {
manaSpentToPay.add(0, mana);
@@ -964,8 +998,10 @@ public class ComputerUtilMana {
* a {@link forge.game.spellability.SpellAbility} object.
* @return a {@link forge.game.mana.Mana} object.
*/
private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid) {
final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard, saBeingPaidFor, restriction, colorsPaid);
private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor,
String restriction, final byte colorsPaid, Map<String, Integer> xManaCostPaidByColor) {
final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard,
saBeingPaidFor, restriction, colorsPaid, xManaCostPaidByColor);
// Exclude border case
if (weightedOptions.isEmpty()) {
@@ -1014,9 +1050,13 @@ public class ComputerUtilMana {
}
private static List<Pair<Mana, Integer>> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard,
final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid) {
final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid, Map<String, Integer> xManaCostPaidByColor) {
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<>();
for (final Mana thisMana : manapool) {
if (shard == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(MagicColor.toShortString(thisMana.getColor()), xManaCostPaidByColor)) {
continue;
}
if (!manapool.canPayForShardWithColor(shard, thisMana.getColor())) {
continue;
}
@@ -1066,7 +1106,6 @@ public class ComputerUtilMana {
getComboManaChoice(ai, saPayment, sa, cost);
}
else if (saPayment.getApi() == ApiType.ManaReflected) {
//System.out.println("Evaluate reflected mana of: " + saPayment.getHostCard());
Set<String> reflected = CardUtil.getReflectableManaColors(saPayment);
for (byte c : MagicColor.WUBRG) {
@@ -1092,7 +1131,7 @@ public class ComputerUtilMana {
}
}
private static boolean canPayShardWithSpellAbility(ManaCostShard toPay, Player ai, SpellAbility ma, SpellAbility sa, boolean checkCosts) {
private static boolean canPayShardWithSpellAbility(ManaCostShard toPay, Player ai, SpellAbility ma, SpellAbility sa, boolean checkCosts, Map<String, Integer> xManaCostPaidByColor) {
final Card sourceCard = ma.getHostCard();
if (isManaSourceReserved(ai, sourceCard, sa)) {
@@ -1130,6 +1169,10 @@ public class ComputerUtilMana {
if (m.isComboMana()) {
for (String s : m.getComboColors().split(" ")) {
if (toPay == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(s, xManaCostPaidByColor)) {
continue;
}
if ("Any".equals(s) || ai.getManaPool().canPayForShardWithColor(toPay, ManaAtom.fromName(s)))
return true;
}
@@ -1140,6 +1183,9 @@ public class ComputerUtilMana {
Set<String> reflected = CardUtil.getReflectableManaColors(ma);
for (byte c : MagicColor.WUBRG) {
if (toPay == ManaCostShard.COLORED_X && !ManaCostBeingPaid.canColoredXShardBePaidByColor(MagicColor.toShortString(c), xManaCostPaidByColor)) {
continue;
}
if (ai.getManaPool().canPayForShardWithColor(toPay, c) && reflected.contains(MagicColor.toLongString(c))) {
m.setExpressChoice(MagicColor.toShortString(c));
return true;
@@ -1147,6 +1193,16 @@ public class ComputerUtilMana {
}
return false;
}
if (toPay == ManaCostShard.COLORED_X) {
for (String s : m.mana().split(" ")) {
if (ManaCostBeingPaid.canColoredXShardBePaidByColor(s, xManaCostPaidByColor)) {
return true;
}
}
return false;
}
return true;
}
@@ -1258,7 +1314,7 @@ public class ComputerUtilMana {
final AbilityManaPart abMana = manaAb.getManaPart();
if (abMana.isComboMana()) {
int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), saRoot) : 1;
int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), manaAb) : 1;
final ManaCostBeingPaid testCost = new ManaCostBeingPaid(cost);
final String[] comboColors = abMana.getComboColors().split(" ");
for (int nMana = 1; nMana <= amount; nMana++) {
@@ -1278,7 +1334,7 @@ public class ComputerUtilMana {
if (!testCost.isPaid()) {
// Loop over combo colors
for (String color : comboColors) {
if (satisfiesColorChoice(abMana, choiceString, choice) && testCost.isAnyPartPayableWith(ManaAtom.fromName(color), ai.getManaPool())) {
if (satisfiesColorChoice(abMana, choiceString, choice) && testCost.needsColor(ManaAtom.fromName(color), ai.getManaPool())) {
payMultipleMana(testCost, color, ai);
if (nMana != 1) {
choiceString.append(" ");
@@ -1426,24 +1482,33 @@ public class ComputerUtilMana {
String restriction = null;
if (payCosts != null && payCosts.getCostMana() != null) {
restriction = payCosts.getCostMana().getRestiction();
restriction = payCosts.getCostMana().getRestriction();
}
ManaCostBeingPaid cost = new ManaCostBeingPaid(mana, restriction);
// Tack xMana Payments into mana here if X is a set value
if (cost.getXcounter() > 0 || extraMana > 0) {
int manaToAdd = 0;
int xCounter = cost.getXcounter();
if (test && extraMana > 0) {
final int multiplicator = Math.max(cost.getXcounter(), 1);
final int multiplicator = Math.max(xCounter, 1);
manaToAdd = extraMana * multiplicator;
} else {
manaToAdd = AbilityUtils.calculateAmount(card, "X", sa) * cost.getXcounter();
manaToAdd = AbilityUtils.calculateAmount(card, "X", sa) * xCounter;
}
cost.increaseShard(ManaCostShard.parseNonGeneric(sa.getParamOrDefault("XColor", "1")), manaToAdd);
String xColor = sa.getParamOrDefault("XColor", "1");
if (card.hasKeyword("Spend only colored mana on X. No more than one mana of each color may be spent this way.")) {
xColor = "WUBRGX";
}
if (xCounter > 0) {
cost.setXManaCostPaid(manaToAdd / xCounter, xColor);
} else {
cost.increaseShard(ManaCostShard.parseNonGeneric(xColor), manaToAdd);
}
if (!test) {
sa.setXManaCostPaid(manaToAdd / cost.getXcounter());
sa.setXManaCostPaid(manaToAdd / xCounter);
}
}
@@ -1530,7 +1595,7 @@ public class ComputerUtilMana {
public boolean apply(final Card c) {
for (final SpellAbility am : getAIPlayableMana(c)) {
am.setActivatingPlayer(ai);
if (!checkPlayable || am.canPlay()) {
if (!checkPlayable || (am.canPlay() && am.checkRestrictions(ai))) {
return true;
}
}
@@ -1834,8 +1899,7 @@ public class ComputerUtilMana {
if (!res.contains(a)) {
if (cost.isReusuableResource()) {
res.add(0, a);
}
else {
} else {
res.add(res.size(), a);
}
}
@@ -1848,7 +1912,7 @@ public class ComputerUtilMana {
final Card offering = sa.getSacrificedAsOffering();
offering.setUsedToPay(false);
if (costIsPaid && !test) {
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, null);
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, null, null);
}
sa.resetSacrificedAsOffering();
}
@@ -1856,7 +1920,7 @@ public class ComputerUtilMana {
final Card emerge = sa.getSacrificedAsEmerge();
emerge.setUsedToPay(false);
if (costIsPaid && !test) {
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, null);
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, null, null);
}
sa.resetSacrificedAsEmerge();
}

View File

@@ -162,9 +162,9 @@ public class CreatureEvaluator implements Function<Card, Integer> {
value -= subValue(10, "must-attack");
} else if (c.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
value -= subValue(10, "must-attack-player");
} else if (c.hasKeyword("CARDNAME can block only creatures with flying.")) {
}/* else if (c.hasKeyword("CARDNAME can block only creatures with flying.")) {
value -= subValue(toughness * 5, "reverse-reach");
}
}//*/
if (c.hasSVar("DestroyWhenDamaged")) {
value -= subValue((toughness - 1) * 9, "dies-to-dmg");

View File

@@ -3,23 +3,12 @@ package forge.ai;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Set;
import com.google.common.collect.*;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import forge.StaticData;
import forge.card.CardStateName;
import forge.card.MagicColor;
@@ -27,7 +16,6 @@ import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey;
import forge.game.ability.effects.DetachedCardEffect;
import forge.game.card.Card;
import forge.game.card.CardCloneStates;
@@ -44,9 +32,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.trigger.TriggerType;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import forge.item.IPaperCard;
@@ -63,6 +49,7 @@ public abstract class GameState {
ZONES.put(ZoneType.Library, "library");
ZONES.put(ZoneType.Exile, "exile");
ZONES.put(ZoneType.Command, "command");
ZONES.put(ZoneType.Sideboard, "sideboard");
}
private int humanLife = -1;
@@ -89,7 +76,8 @@ public abstract class GameState {
private final Map<Card, Integer> markedDamage = new HashMap<>();
private final Map<Card, List<String>> cardToChosenClrs = new HashMap<>();
private final Map<Card, CardCollection> cardToChosenCards = new HashMap<>();
private final Map<Card, List<String>> cardToChosenTypes = new HashMap<>();
private final Map<Card, String> cardToChosenType = new HashMap<>();
private final Map<Card, String> cardToChosenType2 = new HashMap<>();
private final Map<Card, List<String>> cardToRememberedId = new HashMap<>();
private final Map<Card, List<String>> cardToImprintedId = new HashMap<>();
private final Map<Card, List<String>> cardToMergedCards = new HashMap<>();
@@ -340,16 +328,15 @@ public abstract class GameState {
newText.append("|Damage:").append(c.getDamage());
}
SpellAbility first = c.getFirstSpellAbility();
if (first != null) {
if (first.hasChosenColor()) {
newText.append("|ChosenColor:").append(TextUtil.join(first.getChosenColors(), ","));
}
if (first.hasChosenType()) {
newText.append("|ChosenType:").append(TextUtil.join(first.getChosenType(), ","));
}
if (!c.getChosenColor().isEmpty()) {
newText.append("|ChosenColor:").append(TextUtil.join(c.getChosenColors(), ","));
}
if (!c.getChosenType().isEmpty()) {
newText.append("|ChosenType:").append(c.getChosenType());
}
if (!c.getChosenType2().isEmpty()) {
newText.append("|ChosenType2:").append(c.getChosenType2());
}
if (!c.getNamedCard().isEmpty()) {
newText.append("|NamedCard:").append(c.getNamedCard());
}
@@ -412,6 +399,12 @@ public abstract class GameState {
// Need to figure out a better way to detect if it's actually on adventure.
newText.append("|OnAdventure");
}
if (c.isForetold()) {
newText.append("|Foretold");
}
if (c.isForetoldThisTurn()) {
newText.append("|ForetoldThisTurn");
}
}
@@ -579,6 +572,13 @@ public abstract class GameState {
aiCardTexts.put(ZoneType.Command, categoryValue);
}
else if (categoryName.endsWith("sideboard")) {
if (isHuman)
humanCardTexts.put(ZoneType.Sideboard, categoryValue);
else
aiCardTexts.put(ZoneType.Sideboard, categoryValue);
}
else if (categoryName.startsWith("ability")) {
abilityString.put(categoryName.substring("ability".length()), categoryValue);
}
@@ -638,7 +638,8 @@ public abstract class GameState {
markedDamage.clear();
cardToChosenClrs.clear();
cardToChosenCards.clear();
cardToChosenTypes.clear();
cardToChosenType.clear();
cardToChosenType2.clear();
cardToMergedCards.clear();
cardToScript.clear();
cardAttackMap.clear();
@@ -733,7 +734,7 @@ public abstract class GameState {
if (persistent) {
produced.put("PersistentMana", "True");
}
final AbilityManaPart abMana = new AbilityManaPart(dummy, null, produced);
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced);
game.getAction().invoke(new Runnable() {
@Override
public void run() {
@@ -769,23 +770,10 @@ public abstract class GameState {
}
game.fireEvent(new GameEventAttackersDeclared(attackingPlayer, attackersMap));
if (!combat.getAttackers().isEmpty()) {
List<GameEntity> attackedTarget = Lists.newArrayList();
for (final Card c : combat.getAttackers()) {
attackedTarget.add(combat.getDefenderByAttacker(c));
}
final Map<AbilityKey, Object> runParams = Maps.newEnumMap(AbilityKey.class);
runParams.put(AbilityKey.Attackers, combat.getAttackers());
runParams.put(AbilityKey.AttackingPlayer, combat.getAttackingPlayer());
runParams.put(AbilityKey.AttackedTarget, attackedTarget);
game.getTriggerHandler().runTrigger(TriggerType.AttackersDeclared, runParams, false);
}
for (final Card c : combat.getAttackers()) {
CombatUtil.checkDeclaredAttacker(game, c, combat);
CombatUtil.checkDeclaredAttacker(game, c, combat, false);
}
game.getTriggerHandler().resetActiveTriggers();
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged());
@@ -828,7 +816,6 @@ public abstract class GameState {
Card exiledWith = idToCard.get(Integer.parseInt(id));
c.setExiledWith(exiledWith);
c.setExiledBy(exiledWith.getController());
exiledWith.addExiledWith(c);
}
}
@@ -1087,13 +1074,19 @@ public abstract class GameState {
Card c = entry.getKey();
List<String> colors = entry.getValue();
c.setChosenColors(colors, c.getFirstSpellAbility());
c.setChosenColors(colors);
}
// Chosen type
for (Entry<Card, List<String>> entry : cardToChosenTypes.entrySet()) {
for (Entry<Card, String> entry : cardToChosenType.entrySet()) {
Card c = entry.getKey();
c.setChosenType(entry.getValue(), c.getFirstSpellAbility());
c.setChosenType(entry.getValue());
}
// Chosen type 2
for (Entry<Card, String> entry : cardToChosenType2.entrySet()) {
Card c = entry.getKey();
c.setChosenType2(entry.getValue());
}
// Named card
@@ -1196,7 +1189,7 @@ public abstract class GameState {
String[] allCounterStrings = counterString.split(",");
for (final String counterPair : allCounterStrings) {
String[] pair = counterPair.split("=", 2);
entity.addCounter(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, false, false, null);
entity.addCounter(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, null, false, false, null);
}
}
@@ -1236,8 +1229,7 @@ public abstract class GameState {
boolean tapped = c.isTapped();
boolean sickness = c.hasSickness();
Map<CounterType, Integer> counters = c.getCounters();
// Note: Not clearCounters() since we want to keep the counters
// var as-is.
// Note: Not clearCounters() since we want to keep the counters var as-is.
c.setCounters(Maps.newHashMap());
if (c.isAura()) {
// dummy "enchanting" to indicate that the card will be force-attached elsewhere
@@ -1340,7 +1332,7 @@ public abstract class GameState {
}
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);
SpellAbility saAdventure = 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.");
@@ -1379,7 +1371,9 @@ public abstract class GameState {
} else if (info.startsWith("ChosenColor:")) {
cardToChosenClrs.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
} else if (info.startsWith("ChosenType:")) {
cardToChosenTypes.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
cardToChosenType.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("ChosenType2:")) {
cardToChosenType2.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("ChosenCards:")) {
CardCollection chosen = new CardCollection();
String[] idlist = info.substring(info.indexOf(':') + 1).split(",");
@@ -1411,6 +1405,14 @@ public abstract class GameState {
}
} else if (info.equals("NoETBTrigs")) {
cardsWithoutETBTrigs.add(c);
} else if (info.equals("Foretold")) {
c.setForetold(true);
c.turnFaceDown(true);
c.addMayLookTemp(c.getOwner());
} else if (info.equals("ForetoldThisTurn")) {
c.setForetoldThisTurn(true);
} else if (info.equals("IsToken")) {
c.setToken(true);
}
}

View File

@@ -8,6 +8,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
@@ -79,6 +80,7 @@ import forge.util.MyRandom;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
/**
* A prototype for player controller class
*
@@ -129,6 +131,29 @@ public class PlayerControllerAi extends PlayerController {
return ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, defender, overrideOrder);
}
@Override
public Map<GameEntity, Integer> divideShield(Card effectSource, Map<GameEntity, Integer> affected, int shieldAmount) {
// TODO: AI current can't use this so this is not implemented.
return new HashMap<>();
}
@Override
public Map<Byte, Integer> specifyManaCombo(SpellAbility sa, ColorSet colorSet, int manaAmount, boolean different) {
Map<Byte, Integer> result = new HashMap<>();
for (int i = 0; i < manaAmount; ++i) {
Byte chosen = chooseColor("", sa, colorSet);
if (result.containsKey(chosen)) {
result.put(chosen, result.get(chosen) + 1);
} else {
result.put(chosen, 1);
}
if (different) {
colorSet = ColorSet.fromMask(colorSet.getColor() - chosen);
}
}
return result;
}
@Override
public Integer announceRequirements(SpellAbility ability, String announce) {
// For now, these "announcements" are made within the AI classes of the appropriate SA effects
@@ -528,7 +553,14 @@ public class PlayerControllerAi extends PlayerController {
@Override
public CardCollectionView chooseCardsToDiscardUnlessType(int num, CardCollectionView hand, String uType, SpellAbility sa) {
final CardCollectionView cardsOfType = CardLists.getType(hand, uType);
String [] splitUTypes = uType.split(",");
CardCollection cardsOfType = new CardCollection();
for (String part : splitUTypes) {
CardCollection partCards = CardLists.getType(hand, part);
if (!partCards.isEmpty()) {
cardsOfType.addAll(partCards);
}
}
if (!cardsOfType.isEmpty()) {
Card toDiscard = Aggregates.itemWithMin(cardsOfType, CardPredicates.Accessors.fnGetCmc);
return new CardCollection(toDiscard);
@@ -543,12 +575,13 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public List<String> chooseSomeType(String kindOfType, SpellAbility sa, int min, int max, List<String> validTypes) {
List<String> chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), min, max, validTypes);
if (chosen.isEmpty()) {
return validTypes.subList(0, min);
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");
}
getGame().getAction().notifyOfValue(sa, player, chosen.toString(), player);
getGame().getAction().notifyOfValue(sa, player, chosen, player);
return chosen;
}
@@ -558,8 +591,8 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question) {
return brains.aiShouldRun(replacementEffect, effectSA);
public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question) {
return brains.aiShouldRun(replacementEffect, effectSA, affected);
}
@Override
@@ -639,7 +672,6 @@ public class PlayerControllerAi extends PlayerController {
@Override
public boolean playChosenSpellAbility(SpellAbility sa) {
// System.out.println("Playing sa: " + sa);
if (sa instanceof LandAbility) {
if (sa.canPlay()) {
sa.resolve();
@@ -778,7 +810,8 @@ public class PlayerControllerAi extends PlayerController {
case "BetterTgtThanRemembered":
if (source.getRememberedCount() > 0) {
Card rem = (Card) source.getFirstRemembered();
if (!rem.isInZone(ZoneType.Battlefield)) {
// avoid pumping opponent creature
if (!rem.isInZone(ZoneType.Battlefield) || rem.getController().isOpponentOf(source.getController())) {
return true;
}
for (Card c : source.getController().getCreaturesInPlay()) {
@@ -988,6 +1021,7 @@ public class PlayerControllerAi extends PlayerController {
emptyAbility.setActivatingPlayer(player);
emptyAbility.setTriggeringObjects(sa.getTriggeringObjects());
emptyAbility.setSVars(sa.getSVars());
emptyAbility.setXManaCostPaid(sa.getRootAbility().getXManaCostPaid());
if (ComputerUtilCost.willPayUnlessCost(sa, player, cost, alreadyPaid, allPayers) && ComputerUtilCost.canPayCost(emptyAbility, player)) {
ComputerUtil.playNoStack(player, emptyAbility, getGame()); // AI needs something to resolve to pay that cost
return true;
@@ -1019,7 +1053,7 @@ public class PlayerControllerAi extends PlayerController {
*/
if (sa.isMayChooseNewTargets() && !sa.setupTargets()) {
if (sa.isSpell()) {
sa.getHostCard().ceaseToExist();
player.getGame().getAction().ceaseToExist(sa.getHostCard(), false);
}
continue;
}
@@ -1053,14 +1087,13 @@ public class PlayerControllerAi extends PlayerController {
boolean noManaCost = tgtSA.hasParam("WithoutManaCost");
if (tgtSA instanceof Spell) { // Isn't it ALWAYS a spell?
Spell spell = (Spell) tgtSA;
if (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional) {
if (tgtSA.checkRestrictions(brains.getPlayer()) && (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional)) {
if (noManaCost) {
return ComputerUtil.playSpellAbilityWithoutPayingManaCost(player, tgtSA, getGame());
} else {
return ComputerUtil.playStack(tgtSA, player, getGame());
}
} else
return false; // didn't play spell
return ComputerUtil.playStack(tgtSA, player, getGame());
}
return false; // didn't play spell
}
return true;
}
@@ -1083,7 +1116,6 @@ public class PlayerControllerAi extends PlayerController {
boolean allCreatures = Iterables.all(Iterables.concat(pile1, pile2), CardPredicates.Presets.CREATURES);
int cmc1 = allCreatures ? ComputerUtilCard.evaluateCreatureList(pile1) : ComputerUtilCard.evaluatePermanentList(pile1);
int cmc2 = allCreatures ? ComputerUtilCard.evaluateCreatureList(pile2) : ComputerUtilCard.evaluatePermanentList(pile2);
System.out.println("value:" + cmc1 + " " + cmc2);
// for now, this assumes that the outcome will be bad
// TODO: This should really have a ChooseLogic param to

View File

@@ -39,7 +39,6 @@ import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardUtil;
@@ -112,6 +111,63 @@ public class SpecialCardAi {
}
}
// Brain in a Jar
public static class BrainInAJar {
public static boolean consider(final Player ai, final SpellAbility sa) {
final Card source = sa.getHostCard();
int counterNum = source.getCounters(CounterEnumType.CHARGE);
// no need for logic
if (counterNum == 0) {
return false;
}
int libsize = ai.getCardsIn(ZoneType.Library).size();
final CardCollection hand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), Predicates.or(
CardPredicates.isType("Instant"), CardPredicates.isType("Sorcery")));
if (!hand.isEmpty()) {
// has spell that can be cast in hand with put ability
if (!CardLists.filter(hand, CardPredicates.hasCMC(counterNum + 1)).isEmpty()) {
return false;
}
// has spell that can be cast if one counter is removed
if (!CardLists.filter(hand, CardPredicates.hasCMC(counterNum)).isEmpty()) {
sa.setXManaCostPaid(1);
return true;
}
}
final CardCollection library = CardLists.filter(ai.getCardsIn(ZoneType.Library), Predicates.or(
CardPredicates.isType("Instant"), CardPredicates.isType("Sorcery")));
if (!library.isEmpty()) {
// get max cmc of instant or sorceries in the libary
int maxCMC = 0;
for (final Card c : library) {
int v = c.getCMC();
if (c.isSplitCard()) {
v = Math.max(c.getCMC(Card.SplitCMCMode.LeftSplitCMC), c.getCMC(Card.SplitCMCMode.RightSplitCMC));
}
if (v > maxCMC) {
maxCMC = v;
}
}
// there is a spell with more CMC, no need to remove counter
if (counterNum + 1 < maxCMC) {
return false;
}
int maxToRemove = counterNum - maxCMC + 1;
// no Scry 0, even if its catched from later stuff
if (maxToRemove <= 0) {
return false;
}
sa.setXManaCostPaid(maxToRemove);
} else {
// no Instant or Sorceries anymore, just scry
sa.setXManaCostPaid(Math.min(counterNum, libsize));
}
return true;
}
}
// Chain of Acid
public static class ChainOfAcid {
public static boolean consider(final Player ai, final SpellAbility sa) {
@@ -159,10 +215,9 @@ public class SpecialCardAi {
final PhaseHandler ph = ai.getGame().getPhaseHandler();
final Combat combat = ai.getGame().getCombat();
Card animated = AnimateAi.becomeAnimated(sa.getHostCard(), sa);
animated.addType("Creature");
Card animated = AnimateAi.becomeAnimated(sa.getHostCard(), sa.getSubAbility());
if (sa.getHostCard().canReceiveCounters(CounterEnumType.P1P1)) {
animated.addCounter(CounterEnumType.P1P1, 2, ai, false, null);
animated.addCounter(CounterEnumType.P1P1, 2, ai, sa.getSubAbility(), false, null);
}
boolean isOppEOT = ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == ai;
boolean isValuableAttacker = ph.is(PhaseType.MAIN1, ai) && ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, animated);
@@ -170,10 +225,6 @@ public class SpecialCardAi {
return isOppEOT || isValuableAttacker || isValuableBlocker;
}
public static SpellAbility considerAnimating(final Player ai, final SpellAbility sa, final List<SpellAbility> options) {
return ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) ? options.get(0) : options.get(1);
}
}
// Cursed Scroll
@@ -327,7 +378,7 @@ public class SpecialCardAi {
}
// Do not activate if damage will be prevented
if (source.staticDamagePrevention(predictedPT.getLeft(), source, true, true) == 0) {
if (source.staticDamagePrevention(predictedPT.getLeft(), 0, source, true) == 0) {
return false;
}
@@ -911,6 +962,39 @@ public class SpecialCardAi {
}
}
// Multiple Choice
public static class MultipleChoice {
public static boolean consider(final Player ai, final SpellAbility sa) {
int maxX = ComputerUtilCost.getMaxXValue(sa, ai);
if (maxX == 0) {
return false;
}
boolean canScryDraw = maxX >= 1 && ai.getCardsIn(ZoneType.Library).size() >= 3; // TODO: generalize / use profile values
boolean canBounce = maxX >= 2 && !ai.getOpponents().getCreaturesInPlay().isEmpty();
boolean shouldBounce = canBounce && ComputerUtilCard.evaluateCreature(ComputerUtilCard.getWorstCreatureAI(ai.getOpponents().getCreaturesInPlay())) > 210; // 180 is the level of a 4/4 token creature
boolean canMakeToken = maxX >= 3;
boolean canDoAll = maxX >= 4 && canScryDraw && shouldBounce;
if (canDoAll) {
sa.setXManaCostPaid(4);
return true;
} else if (canMakeToken) {
sa.setXManaCostPaid(3);
return true;
} else if (shouldBounce) {
sa.setXManaCostPaid(2);
return true;
} else if (canScryDraw) {
sa.setXManaCostPaid(1);
return true;
}
return false;
}
}
// Necropotence
public static class Necropotence {
public static boolean consider(final Player ai, final SpellAbility sa) {

View File

@@ -58,6 +58,11 @@ public abstract class SpellAbilityAi {
final Card source = sa.getHostCard();
final Cost cost = sa.getPayCosts();
if (sa.hasParam("AICheckCanPlayWithDefinedX")) {
// FIXME: can this somehow be simplified without the need for an extra AI hint?
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai));
}
if (!checkConditions(ai, sa, sa.getConditions())) {
SpellAbility sub = sa.getSubAbility();
if (sub != null && !checkConditions(ai, sub, sub.getConditions())) {
@@ -157,7 +162,7 @@ public abstract class SpellAbilityAi {
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false; // prevent infinite loop
}
return MyRandom.getRandom().nextFloat() < .8f; // random success
return MyRandom.getRandom().nextFloat() < .8f; // random success
}
public final boolean doTriggerAI(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
@@ -188,7 +193,7 @@ public abstract class SpellAbilityAi {
* Handles the AI decision to play a triggered SpellAbility
*/
protected boolean doTriggerAINoCost(final Player aiPlayer, final SpellAbility sa, final boolean mandatory) {
if (canPlayWithoutRestrict(aiPlayer, sa)) {
if (canPlayWithoutRestrict(aiPlayer, sa) && (!mandatory || sa.isTargetNumberValid())) {
return true;
}
@@ -285,7 +290,6 @@ public abstract class SpellAbilityAi {
return false;
}
return phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai);
}

View File

@@ -34,6 +34,7 @@ public enum SpellApiToAi {
.put(ApiType.BidLife, BidLifeAi.class)
.put(ApiType.Bond, BondAi.class)
.put(ApiType.Branch, AlwaysPlayAi.class)
.put(ApiType.Camouflage, ChooseCardAi.class)
.put(ApiType.ChangeCombatants, ChangeCombatantsAi.class)
.put(ApiType.ChangeTargets, ChangeTargetsAi.class)
.put(ApiType.ChangeX, AlwaysPlayAi.class)
@@ -49,6 +50,7 @@ public enum SpellApiToAi {
.put(ApiType.ChooseSource, ChooseSourceAi.class)
.put(ApiType.ChooseType, ChooseTypeAi.class)
.put(ApiType.Clash, ClashAi.class)
.put(ApiType.ClassLevelUp, AlwaysPlayAi.class)
.put(ApiType.Cleanup, AlwaysPlayAi.class)
.put(ApiType.Clone, CloneAi.class)
.put(ApiType.CopyPermanent, CopyPermanentAi.class)
@@ -94,8 +96,10 @@ public enum SpellApiToAi {
.put(ApiType.Haunt, HauntAi.class)
.put(ApiType.ImmediateTrigger, ImmediateTriggerAi.class)
.put(ApiType.Investigate, InvestigateAi.class)
.put(ApiType.Learn, LearnAi.class)
.put(ApiType.LoseLife, LifeLoseAi.class)
.put(ApiType.LosesGame, GameLossAi.class)
.put(ApiType.MakeCard, AlwaysPlayAi.class)
.put(ApiType.Mana, ManaEffectAi.class)
.put(ApiType.ManaReflected, CannotPlayAi.class)
.put(ApiType.Manifest, ManifestAi.class)
@@ -139,6 +143,7 @@ public enum SpellApiToAi {
.put(ApiType.ReplaceEffect, AlwaysPlayAi.class)
.put(ApiType.ReplaceDamage, AlwaysPlayAi.class)
.put(ApiType.ReplaceSplitDamage, AlwaysPlayAi.class)
.put(ApiType.ReplaceToken, AlwaysPlayAi.class)
.put(ApiType.RestartGame, RestartGameAi.class)
.put(ApiType.Reveal, RevealAi.class)
.put(ApiType.RevealHand, RevealHandAi.class)
@@ -168,6 +173,7 @@ public enum SpellApiToAi {
.put(ApiType.UnattachAll, UnattachAllAi.class)
.put(ApiType.Untap, UntapAi.class)
.put(ApiType.UntapAll, UntapAllAi.class)
.put(ApiType.Venture, VentureAi.class)
.put(ApiType.Vote, VoteAi.class)
.put(ApiType.WinsGame, GameWinAi.class)

View File

@@ -21,8 +21,7 @@ public class ActivateAbilityAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard();
final Player opp = ai.getWeakestOpponent();
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
final Player opp = ai.getStrongestOpponent();
List<Card> list = CardLists.getType(opp.getCardsIn(ZoneType.Battlefield), sa.getParamOrDefault("Type", "Card"));
if (list.isEmpty()) {
@@ -40,12 +39,13 @@ public class ActivateAbilityAi extends SpellAbilityAi {
sa.getTargets().add(opp);
}
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
return randomReturn;
}
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Player opp = ai.getWeakestOpponent();
final Player opp = ai.getStrongestOpponent();
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Card source = sa.getHostCard();

View File

@@ -34,7 +34,7 @@ public class AmassAi extends SpellAbilityAi {
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, false);
Card token = TokenInfo.getProtoType(tokenScript, sa, ai, false);
if (token == null) {
return false;

View File

@@ -30,6 +30,7 @@ import forge.game.cost.CostPutCounter;
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;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityContinuous;
@@ -99,30 +100,30 @@ public class AnimateAi extends SpellAbilityAi {
}
// Don't use instant speed animate abilities before AI's COMBAT_BEGIN
if (!ph.is(PhaseType.COMBAT_BEGIN) && ph.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa)
&& !sa.hasParam("ActivationPhases") && !sa.hasParam("Permanent")) {
&& !sa.hasParam("ActivationPhases") && !"Permanent".equals(sa.getParam("Duration"))) {
return false;
}
// Don't use instant speed animate abilities outside human's
// COMBAT_DECLARE_ATTACKERS or if no attackers
if (ph.getPlayerTurn().isOpponentOf(ai) && !sa.hasParam("Permanent")
if (ph.getPlayerTurn().isOpponentOf(ai) && !"Permanent".equals(sa.getParam("Duration"))
&& (!ph.is(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| game.getCombat() != null && game.getCombat().getAttackersOf(ai).isEmpty())) {
return false;
}
// Don't activate during MAIN2 unless this effect is permanent
if (ph.is(PhaseType.MAIN2) && !sa.hasParam("Permanent") && !sa.hasParam("UntilYourNextTurn")) {
if (ph.is(PhaseType.MAIN2) && !"Permanent".equals(sa.getParam("Duration")) && !"UntilYourNextTurn".equals(sa.getParam("Duration"))) {
return false;
}
// Don't animate if the AI won't attack anyway or use as a potential blocker
Player opponent = ai.getWeakestOpponent();
// Activating as a potential blocker is only viable if it's an ability activated from a permanent, otherwise
// the AI will waste resources
boolean activateAsPotentialBlocker = sa.hasParam("UntilYourNextTurn")
boolean activateAsPotentialBlocker = "UntilYourNextTurn".equals(sa.getParam("Duration"))
&& ai.getGame().getPhaseHandler().getNextTurn() != ai
&& source.isPermanent();
if (ph.isPlayerTurn(ai) && ai.getLife() < 6 && opponent.getLife() > 6
&& Iterables.any(opponent.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES)
&& !sa.hasParam("AILogic") && !sa.hasParam("Permanent") && !activateAsPotentialBlocker) {
&& !sa.hasParam("AILogic") && !"Permanent".equals(sa.getParam("Duration")) && !activateAsPotentialBlocker) {
return false;
}
return true;
@@ -155,7 +156,7 @@ public class AnimateAi extends SpellAbilityAi {
&& !c.isEquipping();
// for creatures that could be improved (like Figure of Destiny)
if (!bFlag && c.isCreature() && (sa.hasParam("Permanent") || (!c.isTapped() && !c.isSick()))) {
if (!bFlag && c.isCreature() && ("Permanent".equals(sa.getParam("Duration")) || (!c.isTapped() && !c.isSick()))) {
int power = -5;
if (sa.hasParam("Power")) {
power = AbilityUtils.calculateAmount(c, sa.getParam("Power"), sa);
@@ -178,7 +179,7 @@ public class AnimateAi extends SpellAbilityAi {
}
}
if (!SpellAbilityAi.isSorcerySpeed(sa) && !sa.hasParam("Permanent")) {
if (!SpellAbilityAi.isSorcerySpeed(sa) && !"Permanent".equals(sa.getParam("Duration"))) {
if (sa.hasParam("Crew") && c.isCreature()) {
// Do not try to crew a vehicle which is already a creature
return false;
@@ -243,6 +244,11 @@ public class AnimateAi extends SpellAbilityAi {
return true;
}
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return player.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2);
}
private boolean animateTgtAI(final SpellAbility sa) {
final Player ai = sa.getActivatingPlayer();
final PhaseHandler ph = ai.getGame().getPhaseHandler();
@@ -272,7 +278,7 @@ public class AnimateAi extends SpellAbilityAi {
Map<Card, Integer> data = Maps.newHashMap();
for (final Card c : list) {
// don't use Permanent animate on something that would leave the field
if (c.hasSVar("EndOfTurnLeavePlay") && sa.hasParam("Permanent")) {
if (c.hasSVar("EndOfTurnLeavePlay") && "Permanent".equals(sa.getParam("Duration"))) {
continue;
}
@@ -306,9 +312,9 @@ public class AnimateAi extends SpellAbilityAi {
// if its player turn,
// check if its Permanent or that creature would attack
if (ph.isPlayerTurn(ai)) {
if (!sa.hasParam("Permanent")
if (!"Permanent".equals(sa.getParam("Duration"))
&& !ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, animatedCopy)
&& !sa.hasParam("UntilHostLeavesPlay")) {
&& !"UntilHostLeavesPlay".equals(sa.getParam("Duration"))) {
continue;
}
}
@@ -354,8 +360,7 @@ public class AnimateAi extends SpellAbilityAi {
// This is reasonable for now. Kamahl, Fist of Krosa and a sorcery or
// two are the only things
// that animate a target. Those can just use AI:RemoveDeck:All until
// this can do a reasonably
// good job of picking a good target
// this can do a reasonably good job of picking a good target
return false;
}
@@ -401,7 +406,7 @@ public class AnimateAi extends SpellAbilityAi {
// allow ChosenType - overrides anything else specified
if (types.hasSubtype("ChosenType")) {
types.clear();
types.addAll(sa.getChosenType());
types.add(source.getChosenType());
}
final List<String> keywords = Lists.newArrayList();
@@ -432,7 +437,7 @@ public class AnimateAi extends SpellAbilityAi {
if (sa.hasParam("Colors")) {
final String colors = sa.getParam("Colors");
if (colors.equals("ChosenColor")) {
tmpDesc = CardUtil.getShortColorsString(sa.getChosenColors());
tmpDesc = CardUtil.getShortColorsString(source.getChosenColors());
} else {
tmpDesc = CardUtil.getShortColorsString(Lists.newArrayList(Arrays.asList(colors.split(","))));
}

View File

@@ -6,6 +6,7 @@ import java.util.Iterator;
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;
@@ -554,12 +555,7 @@ public class AttachAi extends SpellAbilityAi {
if (!evenBetterList.isEmpty()) {
betterList = evenBetterList;
}
evenBetterList = CardLists.filter(betterList, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.isUntapped();
}
});
evenBetterList = CardLists.filter(betterList, CardPredicates.Presets.UNTAPPED);
if (!evenBetterList.isEmpty()) {
betterList = evenBetterList;
}
@@ -733,17 +729,15 @@ public class AttachAi extends SpellAbilityAi {
*/
private static Card attachAISpecificCardPreference(final SpellAbility sa, final List<Card> list, final boolean mandatory,
final Card attachSource) {
// I know this isn't much better than Hardcoding, but some cards need it for now
final Player ai = sa.getActivatingPlayer();
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
Card chosen = null;
if ("Guilty Conscience".equals(sourceName)) {
chosen = SpecialCardAi.GuiltyConscience.getBestAttachTarget(ai, sa, list);
} else if ("Bonds of Faith".equals(sourceName)) {
chosen = doPumpOrCurseAILogic(ai, sa, list, "Human");
} else if ("Clutch of Undeath".equals(sourceName)) {
chosen = doPumpOrCurseAILogic(ai, sa, list, "Zombie");
} else if (sa.hasParam("AIValid")) {
// TODO: Make the AI recognize which cards to pump based on the card's abilities alone
chosen = doPumpOrCurseAILogic(ai, sa, list, sa.getParam("AIValid"));
}
// If Mandatory (brought directly into play without casting) gotta
@@ -767,7 +761,7 @@ public class AttachAi extends SpellAbilityAi {
int powerBuff = 0;
for (StaticAbility stAb : sa.getHostCard().getStaticAbilities()) {
if ("Card.EquippedBy".equals(stAb.getParam("Affected")) && stAb.hasParam("AddPower")) {
powerBuff = AbilityUtils.calculateAmount(sa.getHostCard(), stAb.getParam("AddPower"), null);
powerBuff = AbilityUtils.calculateAmount(sa.getHostCard(), stAb.getParam("AddPower"), stAb);
}
}
if (combat != null && combat.isAttacking(equipped) && ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS, sa.getActivatingPlayer())) {
@@ -990,7 +984,7 @@ public class AttachAi extends SpellAbilityAi {
List<GameObject> targets = new ArrayList<>();
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt == null) {
targets = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
targets = AbilityUtils.getDefinedObjects(card, sa.getParam("Defined"), sa);
} else {
AttachAi.attachPreference(sa, tgt, mandatory);
targets = sa.getTargets();
@@ -1129,8 +1123,7 @@ public class AttachAi extends SpellAbilityAi {
for (Card target : list) {
for (Trigger t : target.getTriggers()) {
if (t.getMode() == TriggerType.SpellCast) {
final Map<String, String> params = t.getMapParams();
if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer"))) {
if ("Card.Self".equals(t.getParam("TargetsValid")) && "You".equals(t.getParam("ValidActivatingPlayer"))) {
magnetList.add(target);
break;
}
@@ -1351,7 +1344,7 @@ public class AttachAi extends SpellAbilityAi {
CardCollection list = null;
if (tgt == null) {
list = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
list = AbilityUtils.getDefinedCards(attachSource, sa.getParam("Defined"), sa);
} else {
list = CardLists.getValidCards(aiPlayer.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), sa.getActivatingPlayer(), attachSource, sa);
@@ -1646,7 +1639,7 @@ public class AttachAi extends SpellAbilityAi {
return ComputerUtilCombat.canAttackNextTurn(card) && card.getNetCombatDamage() >= 1;
} else if (keyword.endsWith("CARDNAME attacks each turn if able.") || keyword.endsWith("CARDNAME attacks each combat if able.")) {
return ComputerUtilCombat.canAttackNextTurn(card) && CombatUtil.canBlock(card, true) && !ai.getCreaturesInPlay().isEmpty();
} else if (keyword.endsWith("CARDNAME can't block.") || keyword.contains("CantBlock")) {
} else if (keyword.endsWith("CARDNAME can't block.")) {
return CombatUtil.canBlock(card, true);
} else if (keyword.endsWith("CARDNAME's activated abilities can't be activated.")) {
for (SpellAbility ability : card.getSpellAbilities()) {
@@ -1699,7 +1692,7 @@ public class AttachAi extends SpellAbilityAi {
if (!c.getController().equals(ai)) {
return false;
}
return c.getType().hasCreatureType(type);
return c.isValid(type, ai, sa.getHostCard(), sa);
}
});
List<Card> oppNonType = CardLists.filter(list, new Predicate<Card>() {
@@ -1709,7 +1702,7 @@ public class AttachAi extends SpellAbilityAi {
if (c.getController().equals(ai)) {
return false;
}
return !c.getType().hasCreatureType(type) && !ComputerUtilCard.isUselessCreature(ai, c);
return !c.isValid(type, ai, sa.getHostCard(), sa) && !ComputerUtilCard.isUselessCreature(ai, c);
}
});

View File

@@ -13,18 +13,21 @@ public class BalanceAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
String logic = sa.getParam("AILogic");
int diff = 0;
// TODO Add support for multiplayer logic
final Player opp = aiPlayer.getWeakestOpponent();
final CardCollectionView humPerms = opp.getCardsIn(ZoneType.Battlefield);
Player opp = aiPlayer.getWeakestOpponent();
final CardCollectionView compPerms = aiPlayer.getCardsIn(ZoneType.Battlefield);
for (Player min : aiPlayer.getOpponents()) {
if (min.getCardsIn(ZoneType.Battlefield).size() < opp.getCardsIn(ZoneType.Battlefield).size()) {
opp = min;
}
}
final CardCollectionView humPerms = opp.getCardsIn(ZoneType.Battlefield);
if ("BalanceCreaturesAndLands".equals(logic)) {
// Copied over from hardcoded Balance. We should be checking value of the lands/creatures not just counting
// TODO Copied over from hardcoded Balance. We should be checking value of the lands/creatures for each opponent, not just counting
diff += CardLists.filter(humPerms, CardPredicates.Presets.LANDS).size() -
CardLists.filter(compPerms, CardPredicates.Presets.LANDS).size();
diff += 1.5 * ( CardLists.filter(humPerms, CardPredicates.Presets.CREATURES).size() -
diff += 1.5 * (CardLists.filter(humPerms, CardPredicates.Presets.CREATURES).size() -
CardLists.filter(compPerms, CardPredicates.Presets.CREATURES).size());
}
else if ("BalancePermanents".equals(logic)) {

View File

@@ -2,6 +2,7 @@ package forge.ai.ability;
import java.util.List;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
@@ -24,7 +25,7 @@ public class BidLifeAi extends SpellAbilityAi {
if (tgt != null) {
sa.resetTargets();
if (tgt.canTgtCreature()) {
List<Card> list = CardLists.getTargetableCards(aiPlayer.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), sa);
List<Card> list = CardLists.getTargetableCards(AiAttackController.choosePreferredDefenderPlayer(aiPlayer).getCardsIn(ZoneType.Battlefield), sa);
list = CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source, sa);
if (list.isEmpty()) {
return false;

View File

@@ -10,6 +10,7 @@ import forge.game.card.Card;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetChoices;
public class ChangeTargetsAi extends SpellAbilityAi {
@@ -40,18 +41,20 @@ public class ChangeTargetsAi extends SpellAbilityAi {
// nothing on stack, so nothing to target
return false;
}
final TargetChoices topTargets = topSa.getTargets();
final Card topHost = topSa.getHostCard();
if (sa.getTargets().size() != 0) {
if (sa.getTargets().size() != 0 && sa.isTrigger()) {
// something was already chosen before (e.g. in response to a trigger - Mizzium Meddler), so just proceed
return true;
}
if (!topSa.usesTargeting() || topSa.getTargets().getTargetCards().contains(sa.getHostCard())) {
if (!topSa.usesTargeting() || topTargets.getTargetCards().contains(sa.getHostCard())) {
// if this does not target at all or already targets host, no need to redirect it again
return false;
}
for (Card tgt : topSa.getTargets().getTargetCards()) {
for (Card tgt : topTargets.getTargetCards()) {
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(tgt.getName()) && tgt.getController().equals(aiPlayer)) {
// We are already targeting at least one card with the same name (e.g. in presence of 2+ Spellskites),
// no need to retarget again to another one
@@ -59,7 +62,7 @@ public class ChangeTargetsAi extends SpellAbilityAi {
}
}
if (topSa.getHostCard() != null && !topSa.getHostCard().getController().isOpponentOf(aiPlayer)) {
if (topHost != null && !topHost.getController().isOpponentOf(aiPlayer)) {
// make sure not to redirect our own abilities
return false;
}
@@ -80,12 +83,22 @@ public class ChangeTargetsAi extends SpellAbilityAi {
ManaCost normalizedMana = manaCost.getNormalizedMana();
boolean canPay = ComputerUtilMana.canPayManaCost(new ManaCostBeingPaid(normalizedMana), sa, aiPlayer);
if (potentialDmg != -1 && potentialDmg <= payDamage && !canPay
&& topSa.getTargets().contains(aiPlayer)) {
&& topTargets.contains(aiPlayer)) {
// do not pay Phyrexian mana if the spell is a damaging one but it deals less damage or the same damage as we'll pay life
return false;
}
}
Card firstCard = topTargets.getFirstTargetedCard();
// if we're not the target don't intervene unless we can steal a buff
if (firstCard != null && !aiPlayer.equals(firstCard.getController()) && !topHost.getController().equals(firstCard.getController()) && !topHost.getController().getAllies().contains(firstCard.getController())) {
return false;
}
Player firstPlayer = topTargets.getFirstTargetedPlayer();
if (firstPlayer != null && !aiPlayer.equals(firstPlayer)) {
return false;
}
sa.resetTargets();
sa.getTargets().add(topSa);
return true;

View File

@@ -15,6 +15,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ai.AiAttackController;
import forge.ai.AiBlockController;
import forge.ai.AiCardMemory;
import forge.ai.AiController;
@@ -57,6 +58,7 @@ import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbilityMustTarget;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
@@ -74,11 +76,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
@Override
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
Card host = sa.getHostCard();
if (host != null && host.hasSVar("AIPreferenceOverride")) {
if (sa.getHostCard() != null && sa.getHostCard().hasSVar("AIPreferenceOverride")) {
// currently used by SacAndUpgrade logic, might need simplification
host.removeSVar("AIPreferenceOverride");
sa.getHostCard().removeSVar("AIPreferenceOverride");
}
if (aiLogic.equals("BeforeCombat")) {
@@ -92,7 +92,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} else if (aiLogic.equals("PriorityOptionalCost")) {
boolean highPriority = false;
// if we have more than one of these in hand, might not be worth waiting for optional cost payment on the additional copy
highPriority |= CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals(host.getName())).size() > 1;
highPriority |= CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals(sa.getHostCard().getName())).size() > 1;
// if we are in danger in combat, no need to wait to pay the optional cost
highPriority |= ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ai.getGame().getCombat() != null && ComputerUtilCombat.lifeInDanger(ai, ai.getGame().getCombat());
@@ -127,40 +127,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
return true;
} else if (aiLogic.equals("Pongify")) {
return SpecialAiLogic.doPongifyLogic(ai, sa);
} else if (aiLogic.equals("Ashiok")) {
final int loyalty = host.getCurrentLoyalty() - 1;
CardCollectionView choices = new CardCollection();
for (int i = loyalty; i >= 0; i--) {
sa.setXManaCostPaid(i);
choices = ai.getGame().getCardsIn(ZoneType.listValueOf(sa.getParam("Origin")));
choices = CardLists.getValidCards(choices, sa.getParam("ChangeType"), host.getController(), host, sa);
if (!choices.isEmpty()) {
return true;
}
}
return false;
} else if (aiLogic.equals("BestCard")) {
CardCollectionView choices = ai.getGame().getCardsIn(ZoneType.listValueOf(sa.getParam("Origin")));
choices = CardLists.getValidCards(choices, sa.getParam("ChangeType"), host.getController(), host, sa);
if (!choices.isEmpty()) {
return true;
}
} else if (aiLogic.startsWith("DiscardAllAndRetExiled")) {
int numExiledWithSrc = CardLists.filter(ai.getCardsIn(ZoneType.Exile), CardPredicates.isExiledWith(host)).size();
int curHandSize = ai.getCardsIn(ZoneType.Hand).size();
// minimum card advantage unless the hand will be fully reloaded
int minAdv = aiLogic.contains(".minAdv") ? Integer.parseInt(aiLogic.substring(aiLogic.indexOf(".minAdv") + 7)) : 0;
if (numExiledWithSrc > curHandSize) {
if (ComputerUtil.predictThreatenedObjects(ai, sa, true).contains(host)) {
// Try to gain some card advantage if the card will die anyway
// TODO: ideally, should evaluate the hand value and not discard good hands to it
return true;
}
}
return (curHandSize + minAdv - 1 < numExiledWithSrc) || (numExiledWithSrc >= ai.getMaxHandSize());
}
return super.checkAiLogic(ai, sa, aiLogic);
@@ -197,16 +163,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
return SpecialCardAi.MazesEnd.consider(aiPlayer, sa);
} else if (aiLogic.equals("Pongify")) {
return sa.isTargetNumberValid(); // Pre-targeted in checkAiLogic
} else if (aiLogic.equals("Ashiok")) {
return true; // If checkAiLogic returns true, then we should be good to go
} else if (aiLogic.equals("BestCard")) {
return true; // If checkAiLogic returns true, then we should be good to go
} else if (aiLogic.startsWith("DiscardAllAndRetExiled")) {
return true; // If checkAiLogic returns true, then we should be good to go
}
}
if (isHidden(sa)) {
if (sa.isHidden()) {
return hiddenOriginCanPlayAI(aiPlayer, sa);
}
return knownOriginCanPlayAI(aiPlayer, sa);
@@ -223,21 +182,12 @@ public class ChangeZoneAi extends SpellAbilityAi {
*/
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
if (isHidden(sa)) {
if (sa.isHidden()) {
return hiddenOriginPlayDrawbackAI(aiPlayer, sa);
}
return knownOriginPlayDrawbackAI(aiPlayer, sa);
}
private static boolean isHidden(SpellAbility sa) {
boolean hidden = sa.hasParam("Hidden");
if (!hidden && sa.hasParam("Origin")) {
hidden = ZoneType.isHidden(sa.getParam("Origin"));
}
return hidden;
}
/**
* <p>
* changeZoneTriggerAINoCost.
@@ -273,7 +223,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
return delta <= 0;
}
if (isHidden(sa)) {
if (sa.isHidden()) {
return hiddenTriggerAI(aiPlayer, sa, mandatory);
}
return knownOriginTriggerAI(aiPlayer, sa, mandatory);
@@ -306,7 +256,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
final Card source = sa.getHostCard();
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
ZoneType origin = null;
final Player opponent = ai.getWeakestOpponent();
final Player opponent = AiAttackController.choosePreferredDefenderPlayer(ai);
boolean activateForCost = ComputerUtil.activateForCost(sa, ai);
if (sa.hasParam("Origin")) {
@@ -514,7 +464,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
// if putting cards from hand to library and parent is drawing cards
// make sure this will actually do something:
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Player opp = aiPlayer.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(aiPlayer);
if (tgt != null && tgt.canTgtPlayer()) {
boolean isCurse = sa.isCurse();
if (isCurse && sa.canTarget(opp)) {
@@ -573,7 +523,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
Iterable<Player> pDefined;
final TargetRestrictions tgt = sa.getTargetRestrictions();
if ((tgt != null) && tgt.canTgtPlayer()) {
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (sa.isCurse()) {
if (sa.canTarget(opp)) {
sa.getTargets().add(opp);
@@ -829,7 +779,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
return ph.getNextTurn().equals(ai) && ph.is(PhaseType.END_OF_TURN);
}
if (isHidden(sa)) {
if (sa.isHidden()) {
return true;
}
@@ -935,7 +885,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
// TODO need to set XManaCostPaid for targets, maybe doesn't need PayX anymore?
sa.setXManaCostPaid(xPay);
// TODO since change of PayX. the shouldCastLessThanMax logic might be faulty
}
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(origin), sa);
@@ -956,9 +905,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (sa.isSpell()) {
list.remove(source); // spells can't target their own source, because it's actually in the stack zone
}
//System.out.println("isPreferredTarget " + list);
if (sa.hasParam("AttachedTo")) {
//System.out.println("isPreferredTarget att " + list);
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -970,7 +917,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
return false;
}
});
//System.out.println("isPreferredTarget ok " + list);
}
if (list.size() < sa.getMinTargets()) {
@@ -1206,6 +1152,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (!list.isEmpty()) {
if (destination.equals(ZoneType.Battlefield) || origin.contains(ZoneType.Battlefield)) {
// filter by MustTarget requirement
CardCollection originalList = new CardCollection(list);
boolean mustTargetFiltered = StaticAbilityMustTarget.filterMustTargetCards(ai, list, sa);
final Card mostExpensive = ComputerUtilCard.getMostExpensivePermanentAI(list, sa, false);
if (mostExpensive.isCreature()) {
// if a creature is most expensive take the best one
@@ -1234,6 +1184,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
return false;
}
}
// Restore original list for next loop if filtered by MustTarget requirement
if (mustTargetFiltered) {
list = originalList;
}
} else if (destination.equals(ZoneType.Hand) || destination.equals(ZoneType.Library)) {
List<Card> nonLands = CardLists.getNotType(list, "Land");
// Prefer to pull a creature, generally more useful for AI.
@@ -1352,8 +1307,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
private static Card canBouncePermanent(final Player ai, SpellAbility sa, CardCollectionView list) {
Game game = ai.getGame();
// filter out untargetables
CardCollectionView aiPermanents = CardLists
.filterControlledBy(list, ai);
CardCollectionView aiPermanents = CardLists.filterControlledBy(list, ai);
CardCollection aiPlaneswalkers = CardLists.filter(aiPermanents, Presets.PLANESWALKERS);
// Felidar Guardian + Saheeli Rai combo support
@@ -1525,9 +1479,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
if ("DeathgorgeScavenger".equals(logic)) {
return SpecialCardAi.DeathgorgeScavenger.consider(ai, sa);
} else if ("ExtraplanarLens".equals(logic)) {
}
if ("ExtraplanarLens".equals(logic)) {
return SpecialCardAi.ExtraplanarLens.consider(ai, sa);
} else if ("ExileCombatThreat".equals(logic)) {
}
if ("ExileCombatThreat".equals(logic)) {
return doExileCombatThreatLogic(ai, sa);
}
@@ -1753,7 +1709,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
return true;
}
@Override
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
@@ -1904,23 +1859,25 @@ public class ChangeZoneAi extends SpellAbilityAi {
}));
}
// JAVA 1.8 use Map.Entry.comparingByValue() somehow
Map.Entry<Player, Map.Entry<String, Integer>> max = Collections.max(data.entrySet(), new Comparator<Map.Entry<Player, Map.Entry<String, Integer>>>() {
@Override
public int compare(Map.Entry<Player, Map.Entry<String, Integer>> o1, Map.Entry<Player, Map.Entry<String, Integer>> o2) {
return o1.getValue().getValue() - o2.getValue().getValue();
if (!data.isEmpty()) {
// JAVA 1.8 use Map.Entry.comparingByValue() somehow
Map.Entry<Player, Map.Entry<String, Integer>> max = Collections.max(data.entrySet(), new Comparator<Map.Entry<Player, Map.Entry<String, Integer>>>() {
@Override
public int compare(Map.Entry<Player, Map.Entry<String, Integer>> o1, Map.Entry<Player, Map.Entry<String, Integer>> o2) {
return o1.getValue().getValue() - o2.getValue().getValue();
}
});
// filter list again by the opponent and a creature of the wanted name that can be targeted
list = CardLists.filter(CardLists.filterControlledBy(list, max.getKey()),
CardPredicates.nameEquals(max.getValue().getKey()), CardPredicates.isTargetableBy(sa));
// list should contain only one element or zero
sa.resetTargets();
for (Card c : list) {
sa.getTargets().add(c);
return true;
}
});
// filter list again by the opponent and a creature of the wanted name that can be targeted
list = CardLists.filter(CardLists.filterControlledBy(list, max.getKey()),
CardPredicates.nameEquals(max.getValue().getKey()), CardPredicates.isTargetableBy(sa));
// list should contain only one element or zero
sa.resetTargets();
for (Card c : list) {
sa.getTargets().add(c);
return true;
}
return false;
@@ -2021,17 +1978,12 @@ public class ChangeZoneAi extends SpellAbilityAi {
boolean setPayX = false;
if (unlessCost.equals("X") && sa.getSVar(unlessCost).equals("Count$xPaid")) {
setPayX = true;
// TODO use ComputerUtilCost.getMaxXValue if able
toPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
toPay = ComputerUtilCost.getMaxXValue(sa, ai);
} else {
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
}
if (toPay == 0) {
canBeSaved.add(potentialTgt);
}
if (toPay <= usableManaSources) {
if (toPay == 0 || toPay <= usableManaSources) {
canBeSaved.add(potentialTgt);
}

View File

@@ -245,8 +245,25 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
&& !ComputerUtil.isPlayingReanimator(ai);
}
} else if (origin.equals(ZoneType.Exile)) {
// TODO: nothing to do here at the moment
return false;
String logic = sa.getParam("AILogic");
if (logic != null && logic.startsWith("DiscardAllAndRetExiled")) {
int numExiledWithSrc = CardLists.filter(ai.getCardsIn(ZoneType.Exile), CardPredicates.isExiledWith(source)).size();
int curHandSize = ai.getCardsIn(ZoneType.Hand).size();
// minimum card advantage unless the hand will be fully reloaded
int minAdv = logic.contains(".minAdv") ? Integer.parseInt(logic.substring(logic.indexOf(".minAdv") + 7)) : 0;
if (numExiledWithSrc > curHandSize) {
if (ComputerUtil.predictThreatenedObjects(ai, sa, true).contains(source)) {
// Try to gain some card advantage if the card will die anyway
// TODO: ideally, should evaluate the hand value and not discard good hands to it
return true;
}
}
return (curHandSize + minAdv - 1 < numExiledWithSrc) || (numExiledWithSrc >= ai.getMaxHandSize());
}
} else if (origin.equals(ZoneType.Stack)) {
// time stop can do something like this:
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip

View File

@@ -23,21 +23,23 @@ import forge.util.collect.FCollection;
public class CharmAi extends SpellAbilityAi {
@Override
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
// sa is Entwined, no need for extra logic
if (sa.isEntwine()) {
return true;
}
final Card source = sa.getHostCard();
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
final int num = AbilityUtils.calculateAmount(source, sa.getParamOrDefault("CharmNum", "1"), sa);
final int min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParamOrDefault("MinCharmNum", "1"), sa) : num;
final int num;
final int min;
if (sa.isEntwine()) {
num = min = choices.size();
}
else {
num = AbilityUtils.calculateAmount(source, sa.getParamOrDefault("CharmNum", "1"), sa);
min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParamOrDefault("MinCharmNum", "1"), sa) : num;
}
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
// Reset the chosen list otherwise it will be locked in forever by earlier calls
sa.setChosenList(null);
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
List<AbilitySub> chosenList;
if (!ai.equals(sa.getActivatingPlayer())) {
@@ -159,7 +161,7 @@ public class CharmAi extends SpellAbilityAi {
chosenList.add(allyTainted ? gain : lose);
} else if (oppTainted || ai.getGame().isCardInPlay("Rain of Gore")) {
// Rain of Gore does negate lifegain, so don't benefit the others
// same for if a oppoent does control Tainted Remedy
// same for if a opponent does control Tainted Remedy
// but if ai cant gain life, the effects are negated
chosenList.add(ai.canGainLife() ? lose : gain);
} else if (ai.getGame().isCardInPlay("Sulfuric Vortex")) {
@@ -177,13 +179,13 @@ public class CharmAi extends SpellAbilityAi {
chosenList.add(gain);
} else if(!ai.canGainLife() && aiLife == 14 ) {
// ai cant gain life, but try to avoid falling to 13
// but if a oppoent does control Tainted Remedy its irrelevant
// but if a opponent does control Tainted Remedy its irrelevant
chosenList.add(oppTainted ? lose : gain);
} else if (allyTainted) {
// Tainted Remedy negation logic, try gain instead of lose
// because negation does turn it into lose for opponents
boolean oppCritical = false;
// an oppoent is Critical = 14, and can't gain life, try to lose life instead
// an opponent is Critical = 14, and can't gain life, try to lose life instead
// but only if ai doesn't kill itself with that.
if (aiLife != 14) {
for (Player p : opponents) {
@@ -197,7 +199,7 @@ public class CharmAi extends SpellAbilityAi {
} else {
// normal logic, try to gain life if its critical
boolean oppCritical = false;
// an oppoent is Critical = 12, and can gain life, try to gain life instead
// an opponent is Critical = 12, and can gain life, try to gain life instead
// but only if ai doesn't kill itself with that.
if (aiLife != 12) {
for (Player p : opponents) {
@@ -224,10 +226,12 @@ public class CharmAi extends SpellAbilityAi {
goodChoice = sub;
} else {
// Standard canPlayAi()
sub.setActivatingPlayer(ai);
sub.getRestrictions().setZone(sub.getParent().getRestrictions().getZone());
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
chosenList.add(sub);
if (chosenList.size() == min) {
break; // enough choices
break; // enough choices
}
}
}

View File

@@ -9,6 +9,7 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
@@ -20,6 +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.CounterEnumType;
import forge.game.combat.Combat;
import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType;
@@ -73,10 +75,8 @@ public class ChooseCardAi extends SpellAbilityAi {
return !choices.isEmpty();
} else if (aiLogic.equals("AtLeast2") || aiLogic.equals("BestBlocker")) {
return choices.size() >= 2;
} else if (aiLogic.equals("Clone") || aiLogic.equals("Vesuva")) {
final String filter = aiLogic.equals("Clone") ? "Permanent.YouDontCtrl,Permanent.nonLegendary"
: "Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
} else if (aiLogic.equals("Clone")) {
final String filter = "Permanent.YouDontCtrl,Permanent.nonLegendary";
choices = CardLists.getValidCards(choices, filter, host.getController(), host, sa);
return !choices.isEmpty();
} else if (aiLogic.equals("Never")) {
@@ -96,12 +96,24 @@ public class ChooseCardAi extends SpellAbilityAi {
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
}
});
return !choices.isEmpty();
} else if (aiLogic.equals("Ashiok")) {
final int loyalty = host.getCounters(CounterEnumType.LOYALTY) - 1;
for (int i = loyalty; i >= 0; i--) {
sa.setXManaCostPaid(i);
choices = ai.getGame().getCardsIn(choiceZone);
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host, sa);
if (!choices.isEmpty()) {
return true;
}
}
return !choices.isEmpty();
} else if (aiLogic.equals("RandomNonLand")) {
return !CardLists.getValidCards(choices, "Card.nonLand", host.getController(), host, sa).isEmpty();
} else if (aiLogic.equals("Duneblast")) {
CardCollection aiCreatures = ai.getCreaturesInPlay();
CardCollection oppCreatures = ai.getWeakestOpponent().getCreaturesInPlay();
CardCollection oppCreatures = AiAttackController.choosePreferredDefenderPlayer(ai).getCreaturesInPlay();
aiCreatures = CardLists.getNotKeyword(aiCreatures, Keyword.INDESTRUCTIBLE);
oppCreatures = CardLists.getNotKeyword(oppCreatures, Keyword.INDESTRUCTIBLE);
@@ -159,18 +171,13 @@ public class ChooseCardAi extends SpellAbilityAi {
options = CardLists.filter(options, Presets.UNTAPPED);
}
choice = ComputerUtilCard.getBestCreatureAI(options);
} else if (logic.equals("Clone") || logic.equals("Vesuva")) {
final String filter = logic.equals("Clone") ? "Permanent.YouDontCtrl,Permanent.nonLegendary" :
"Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
} else if (logic.equals("Clone")) {
final String filter = "Permanent.YouDontCtrl,Permanent.nonLegendary";
CardCollection newOptions = CardLists.getValidCards(options, filter.split(","), ctrl, host, sa);
if (!newOptions.isEmpty()) {
options = newOptions;
}
choice = ComputerUtilCard.getBestAI(options);
if (logic.equals("Vesuva") && "Vesuva".equals(choice.getName())) {
choice = null;
}
} else if ("RandomNonLand".equals(logic)) {
options = CardLists.getValidCards(options, "Card.nonLand", host.getController(), host, sa);
choice = Aggregates.random(options);
@@ -239,7 +246,6 @@ public class ChooseCardAi extends SpellAbilityAi {
return true;
}
});
System.out.println("Tangle Wire" + options + " - " + betterList);
if (!betterList.isEmpty()) {
choice = betterList.get(0);
} else {

View File

@@ -7,6 +7,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.SpecialCardAi;
@@ -34,9 +35,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
}
String logic = sa.getParam("AILogic");
if (logic.equals("MomirAvatar")) {
return SpecialCardAi.MomirVigAvatar.consider(ai, sa);
} else if (logic.equals("CursedScroll")) {
if (logic.equals("CursedScroll")) {
return SpecialCardAi.CursedScroll.consider(ai, sa);
}
@@ -44,7 +43,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
if (tgt != null) {
sa.resetTargets();
if (tgt.canOnlyTgtOpponent()) {
sa.getTargets().add(ai.getWeakestOpponent());
sa.getTargets().add(AiAttackController.choosePreferredDefenderPlayer(ai));
} else {
sa.getTargets().add(ai);
}
@@ -64,7 +63,6 @@ public class ChooseCardNameAi extends SpellAbilityAi {
*/
@Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return ComputerUtilCard.getBestAI(options);
}

View File

@@ -1,5 +1,6 @@
package forge.ai.ability;
import forge.ai.AiAttackController;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -16,7 +17,7 @@ public class ChooseEvenOddAi extends SpellAbilityAi {
TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
sa.resetTargets();
Player opp = aiPlayer.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(aiPlayer);
if (sa.canTarget(opp)) {
sa.getTargets().add(opp);
} else {

View File

@@ -8,7 +8,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
@@ -32,7 +31,6 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbilityCantBeCast;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.collect.FCollection;
@@ -54,8 +52,6 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
}
} else if ("GideonBlackblade".equals(aiLogic)) {
return SpecialCardAi.GideonBlackblade.consider(ai, sa);
} else if ("SoulEcho".equals(aiLogic)) {
return doTriggerAINoCost(ai, sa, true);
} else if ("Always".equals(aiLogic)) {
return true;
}
@@ -72,7 +68,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
*/
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
return checkApiLogic(aiPlayer, sa);
return sa.isTrigger() ? doTriggerAINoCost(aiPlayer, sa, sa.isMandatory()) : checkApiLogic(aiPlayer, sa);
}
@Override
@@ -160,6 +156,22 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
}
}
return others;
} else if ("Counters".equals(logic)) {
// TODO: this code will need generalization if this logic is used for cards other
// than Elspeth Conquers Death with different choice parameters
SpellAbility p1p1 = null, loyalty = null;
for (final SpellAbility sp : spells) {
if (("P1P1").equals(sp.getParam("CounterType"))) {
p1p1 = sp;
} else {
loyalty = sp;
}
}
if (sa.getParent().getTargetCard() != null && sa.getParent().getTargetCard().getType().isPlaneswalker()) {
return loyalty;
} else {
return p1p1;
}
} else if ("Fatespinner".equals(logic)) {
SpellAbility skipDraw = null, /*skipMain = null,*/ skipCombat = null;
for (final SpellAbility sp : spells) {
@@ -230,10 +242,11 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
return allow;
}
SpellAbility firstSpell = imprinted.getFirstSpellAbility();
// check if something would prevent it from casting
if (firstSpell == null || StaticAbilityCantBeCast.cantBeCastAbility(firstSpell, imprinted, owner)) {
return allow;
//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) {
@@ -364,8 +377,6 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
} else if ("Riot".equals(logic)) {
SpellAbility counterSA = spells.get(0), hasteSA = spells.get(1);
return preferHasteForRiot(sa, player) ? hasteSA : counterSA;
} else if ("CrawlingBarrens".equals(logic)) {
return SpecialCardAi.CrawlingBarrens.considerAnimating(player, sa, spells);
}
return spells.get(0); // return first choice if no logic found
}

View File

@@ -1,5 +1,6 @@
package forge.ai.ability;
import forge.ai.AiAttackController;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -16,7 +17,7 @@ public class ChooseNumberAi extends SpellAbilityAi {
TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
sa.resetTargets();
Player opp = aiPlayer.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(aiPlayer);
if (sa.canTarget(opp)) {
sa.getTargets().add(opp);
} else {

View File

@@ -8,6 +8,7 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi;
@@ -54,7 +55,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
sa.resetTargets();
Player opp = ai.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (sa.canTarget(opp)) {
sa.getTargets().add(opp);
} else {

View File

@@ -34,6 +34,7 @@ public class ChooseTypeAi extends SpellAbilityAi {
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Mirror Entity Avatar")) {
return doMirrorEntityLogic(aiPlayer, sa);
}
return !chooseType(sa, aiPlayer.getCardsIn(ZoneType.Battlefield)).isEmpty();
} else if ("MostProminentOppControls".equals(sa.getParam("AILogic"))) {
return !chooseType(sa, aiPlayer.getOpponents().getCardsIn(ZoneType.Battlefield)).isEmpty();
}

View File

@@ -3,13 +3,17 @@ package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicates;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
@@ -52,7 +56,7 @@ public class CloneAi extends SpellAbilityAi {
bFlag |= (!c.isCreature() && !c.isTapped() && !(c.getTurnInZone() == phase.getTurn()));
// for creatures that could be improved (like Figure of Destiny)
if (c.isCreature() && (sa.hasParam("Permanent") || (!c.isTapped() && !c.isSick()))) {
if (c.isCreature() && (!sa.hasParam("Duration") || (!c.isTapped() && !c.isSick()))) {
int power = -5;
if (sa.hasParam("Power")) {
power = AbilityUtils.calculateAmount(source, sa.getParam("Power"), sa);
@@ -68,8 +72,7 @@ public class CloneAi extends SpellAbilityAi {
}
if (!bFlag) { // All of the defined stuff is cloned, not very
// useful
if (!bFlag) { // All of the defined stuff is cloned, not very useful
return false;
}
} else {
@@ -95,11 +98,18 @@ public class CloneAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
Card host = sa.getHostCard();
boolean chance = true;
if (sa.usesTargeting()) {
chance = cloneTgtAI(sa);
} else {
if (sa.hasParam("Choices")) {
CardCollectionView choices = CardLists.getValidCards(host.getGame().getCardsIn(ZoneType.Battlefield),
sa.getParam("Choices"), host.getController(), host, sa);
chance = !choices.isEmpty();
}
}
// Improve AI for triggers. If source is a creature with:
@@ -171,18 +181,18 @@ public class CloneAi extends SpellAbilityAi {
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer, Map<String, Object> params) {
final Card host = sa.getHostCard();
final String name = host.getName();
final Player ctrl = host.getController();
final Card cloneTarget = getCloneTarget(sa);
final boolean isOpp = cloneTarget.getController().isOpponentOf(sa.getActivatingPlayer());
final boolean isVesuva = "Vesuva".equals(host.getName());
final boolean isVesuva = "Vesuva".equals(name) || "Sculpting Steel".equals(name);
final boolean canCloneLegendary = "True".equalsIgnoreCase(sa.getParam("NonLegendary"));
String filter = !isVesuva ? "Permanent.YouDontCtrl,Permanent.nonLegendary"
: "Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
: "Permanent.YouDontCtrl+notnamed" + name + ",Permanent.nonLegendary+notnamed" + name;
// TODO: rewrite this block so that this is done somehow more elegantly
if (canCloneLegendary) {
@@ -201,12 +211,13 @@ public class CloneAi extends SpellAbilityAi {
}
}
Card choice = isOpp ? ComputerUtilCard.getWorstAI(options) : ComputerUtilCard.getBestAI(options);
if (isVesuva && "Vesuva".equals(choice.getName())) {
choice = null;
// prevent loop of choosing copy of same card
if (isVesuva) {
options = CardLists.filter(options, Predicates.not(CardPredicates.sharesNameWith(host)));
}
Card choice = isOpp ? ComputerUtilCard.getWorstAI(options) : ComputerUtilCard.getBestAI(options);
return choice;
}
@@ -234,7 +245,7 @@ public class CloneAi extends SpellAbilityAi {
// Combat_Begin step
if (!ph.is(PhaseType.COMBAT_BEGIN)
&& ph.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa)
&& !sa.hasParam("ActivationPhases") && !sa.hasParam("Permanent")) {
&& !sa.hasParam("ActivationPhases") && sa.hasParam("Duration")) {
return false;
}
@@ -245,6 +256,6 @@ public class CloneAi extends SpellAbilityAi {
}
// don't activate during main2 unless this effect is permanent
return !ph.is(PhaseType.MAIN2) || sa.hasParam("Permanent");
return !ph.is(PhaseType.MAIN2) || !sa.hasParam("Duration");
}
}

View File

@@ -3,6 +3,7 @@ package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
@@ -29,9 +30,8 @@ public class ControlExchangeAi extends SpellAbilityAi {
sa.resetTargets();
CardCollection list =
CardLists.getValidCards(ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
// AI won't try to grab cards that are filtered out of AI decks on
// purpose
CardLists.getValidCards(AiAttackController.choosePreferredDefenderPlayer(ai).getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
// AI won't try to grab cards that are filtered out of AI decks on purpose
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {

View File

@@ -39,6 +39,7 @@ import forge.game.player.PlayerCollection;
import forge.game.player.PlayerPredicates;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbilityMustTarget;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
@@ -67,7 +68,6 @@ import forge.util.Aggregates;
public class ControlGainAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
final List<String> lose = Lists.newArrayList();
if (sa.hasParam("LoseControl")) {
@@ -109,8 +109,7 @@ public class ControlGainAi extends SpellAbilityAi {
}
}
// Don't steal something if I can't Attack without, or prevent it from
// blocking at least
// Don't steal something if I can't Attack without, or prevent it from blocking at least
if (lose.contains("EOT")
&& ai.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
&& !sa.isTrigger()) {
@@ -210,7 +209,12 @@ public class ControlGainAi extends SpellAbilityAi {
}
}
// TODO check life of controller and consider stealing from another opponent so the risk of your army disappearing is spread out
while (t == null) {
// filter by MustTarget requirement
CardCollection originalList = new CardCollection(list);
boolean mustTargetFiltered = StaticAbilityMustTarget.filterMustTargetCards(ai, list, sa);
if (planeswalkers > 0) {
t = ComputerUtilCard.getBestPlaneswalkerAI(list);
} else if (creatures > 0) {
@@ -238,6 +242,11 @@ public class ControlGainAi extends SpellAbilityAi {
enchantments--;
}
// Restore original list for next loop if filtered by MustTarget requirement
if (mustTargetFiltered) {
list = originalList;
}
if (!sa.canTarget(t)) {
list.remove(t);
t = null;
@@ -254,7 +263,6 @@ public class ControlGainAi extends SpellAbilityAi {
}
return true;
}
@Override

View File

@@ -44,7 +44,9 @@ public class CopyPermanentAi extends SpellAbilityAi {
return false;
}
if ("MimicVat".equals(aiLogic)) {
if ("MomirAvatar".equals(aiLogic)) {
return SpecialCardAi.MomirVigAvatar.consider(aiPlayer, sa);
} else if ("MimicVat".equals(aiLogic)) {
return SpecialCardAi.MimicVat.considerCopy(aiPlayer, sa);
} else if ("AtEOT".equals(aiLogic)) {
return ph.is(PhaseType.END_OF_TURN);

View File

@@ -113,8 +113,7 @@ public class CounterAi extends SpellAbilityAi {
boolean setPayX = false;
if (unlessCost.equals("X") && sa.getSVar(unlessCost).equals("Count$xPaid")) {
setPayX = true;
// TODO use ComputerUtilCost.getMaxXValue
toPay = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), usableManaSources + 1);
toPay = Math.min(ComputerUtilCost.getMaxXValue(sa, ai), usableManaSources + 1);
} else {
toPay = AbilityUtils.calculateAmount(source, unlessCost, sa);
}
@@ -124,8 +123,7 @@ public class CounterAi extends SpellAbilityAi {
}
if (toPay <= usableManaSources) {
// If this is a reusable Resource, feel free to play it most of
// the time
// If this is a reusable Resource, feel free to play it most of the time
if (!SpellAbilityAi.playReusable(ai,sa)) {
return false;
}

View File

@@ -29,7 +29,6 @@ import forge.util.collect.FCollection;
public class CountersMoveAi extends SpellAbilityAi {
@Override
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
if (sa.usesTargeting()) {
sa.resetTargets();
if (!moveTgtAI(ai, sa)) {
@@ -83,8 +82,7 @@ public class CountersMoveAi extends SpellAbilityAi {
return true;
}
// something you can't block, try to reduce its
// attack
// something you can't block, try to reduce its attack
if (!ComputerUtilCard.canBeBlockedProfitably(ai, cpy)) {
return true;
}
@@ -119,7 +117,6 @@ public class CountersMoveAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) {
sa.resetTargets();
@@ -237,7 +234,6 @@ public class CountersMoveAi extends SpellAbilityAi {
}
private boolean moveTgtAI(final Player ai, final SpellAbility sa) {
final Card host = sa.getHostCard();
final Game game = ai.getGame();
final String type = sa.getParam("CounterType");
@@ -283,8 +279,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
// do not steal a P1P1 from Undying if it would die
// this way
// do not steal a P1P1 from Undying if it would die this way
if (cType != null && cType.is(CounterEnumType.P1P1) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
}
@@ -372,54 +367,68 @@ public class CountersMoveAi extends SpellAbilityAi {
}
}
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ai);
if (!aiList.isEmpty()) {
List<Card> best = CardLists.filter(aiList, new Predicate<Card>() {
Card lki = CardUtil.getLKICopy(src);
if (cType == null) {
lki.clearCounters();
}
else {
lki.setCounters(cType, 0);
}
// go for opponent when higher value implies debuff
if (ComputerUtilCard.evaluateCreature(src) > ComputerUtilCard.evaluateCreature(lki)) {
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ai);
if (!aiList.isEmpty()) {
List<Card> best = CardLists.filter(aiList, new Predicate<Card>() {
@Override
public boolean apply(Card card) {
// gain from useless
if (ComputerUtilCard.isUselessCreature(ai, card)) {
return false;
}
// source would leave the game
if (card.hasSVar("EndOfTurnLeavePlay")) {
return false;
}
if (cType != null) {
if (cType.is(CounterEnumType.P1P1) && card.hasKeyword(Keyword.UNDYING)) {
return false;
}
if (cType.is(CounterEnumType.M1M1) && card.hasKeyword(Keyword.PERSIST)) {
@Override
public boolean apply(Card card) {
// gain from useless
if (ComputerUtilCard.isUselessCreature(ai, card)) {
return false;
}
if (!card.canReceiveCounters(cType)) {
// source would leave the game
if (card.hasSVar("EndOfTurnLeavePlay")) {
return false;
}
if (cType != null) {
if (cType.is(CounterEnumType.P1P1) && card.hasKeyword(Keyword.UNDYING)) {
return false;
}
if (cType.is(CounterEnumType.M1M1)) {
return false;
}
if (!card.canReceiveCounters(cType)) {
return false;
}
}
return true;
}
});
if (best.isEmpty()) {
best = aiList;
}
Card card = ComputerUtilCard.getBestCreatureAI(best);
if (card != null) {
sa.getTargets().add(card);
return true;
}
});
if (best.isEmpty()) {
best = aiList;
}
Card card = ComputerUtilCard.getBestCreatureAI(best);
if (card != null) {
sa.getTargets().add(card);
return true;
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
|| (sa.getRootAbility().isTrigger() && !sa.getRootAbility().isOptionalTrigger());
if (!isMandatoryTrigger) {
// no good target
return false;
}
}
// move counter to opponents creature but only if you can not steal
// them
// try to move to something useless or something that would leave
// play
// move counter to opponents creature but only if you can not steal them
// try to move to something useless or something that would leave play
List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents());
if (!oppList.isEmpty()) {
List<Card> best = CardLists.filter(oppList, new Predicate<Card>() {
@@ -441,7 +450,7 @@ public class CountersMoveAi extends SpellAbilityAi {
});
if (best.isEmpty()) {
best = aiList;
best = oppList;
}
Card card = ComputerUtilCard.getBestCreatureAI(best);
@@ -455,7 +464,7 @@ public class CountersMoveAi extends SpellAbilityAi {
}
}
// used for multiple sources -> defied
// used for multiple sources -> defined
// or for source -> multiple defined
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,

View File

@@ -120,8 +120,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
// pre filter targetable cards with counters and can receive one of
// them
// pre filter targetable cards with counters and can receive one of them
list = CardLists.filter(list, new Predicate<Card>() {
@Override

View File

@@ -8,8 +8,10 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.ai.AiProps;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.PlayerControllerAi;
import forge.ai.SpellAbilityAi;
import forge.game.GameEntity;
import forge.game.card.Card;
@@ -121,6 +123,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
final CounterType poison = CounterType.get(CounterEnumType.POISON);
boolean aggroAI = (((PlayerControllerAi) ai.getController()).getAi()).getBooleanProperty(AiProps.PLAY_AGGRO);
// 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)) {
@@ -128,7 +131,8 @@ public class CountersProliferateAi extends SpellAbilityAi {
return (T)p;
}
} else {
if (p.getCounters(poison) <= 5 || p.canReceiveCounters(poison)) {
// poison is risky, should not proliferate them in most cases
if ((p.getCounters(poison) <= 5 && aggroAI && p.getCounters(CounterEnumType.EXPERIENCE) + p.getCounters(CounterEnumType.ENERGY) >= 1) || !p.canReceiveCounters(poison)) {
return (T)p;
}
}

View File

@@ -47,6 +47,8 @@ import forge.game.player.PlayerCollection;
import forge.game.player.PlayerPredicates;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.MyRandom;
@@ -62,11 +64,9 @@ public class CountersPutAi extends SpellAbilityAi {
*/
@Override
protected boolean willPayCosts(Player ai, SpellAbility sa, Cost cost, Card source) {
final String type = sa.getParam("CounterType");
final String aiLogic = sa.getParamOrDefault("AILogic", "");
// TODO Auto-generated method stub
if (!super.willPayCosts(ai, sa, cost, source)) {
return false;
}
@@ -223,8 +223,7 @@ public class CountersPutAi extends SpellAbilityAi {
}
if (sa.canTarget(ai)) {
// don't target itself when its forced to add poison
// counters too
// don't target itself when its forced to add poison counters too
if (!ai.getCounters().isEmpty()) {
if (!eachExisting || ai.getPoisonCounters() < 5) {
sa.getTargets().add(ai);
@@ -313,7 +312,12 @@ public class CountersPutAi extends SpellAbilityAi {
} else if (logic.startsWith("MoveCounter")) {
return doMoveCounterLogic(ai, sa, ph);
} else if (logic.equals("CrawlingBarrens")) {
return SpecialCardAi.CrawlingBarrens.consider(ai, sa);
boolean willActivate = SpecialCardAi.CrawlingBarrens.consider(ai, sa);
if (willActivate && ph.getPhase().isBefore(PhaseType.MAIN2)) {
// don't use this for mana until after combat
AiCardMemory.rememberCard(ai, source, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2);
}
return willActivate;
}
if (!sa.metConditions() && sa.getSubAbility() == null) {
@@ -401,19 +405,37 @@ public class CountersPutAi extends SpellAbilityAi {
}
if ("Polukranos".equals(logic)) {
boolean found = false;
for (Trigger tr : source.getTriggers()) {
if (!tr.getMode().equals(TriggerType.BecomeMonstrous)) {
continue;
}
SpellAbility oa = tr.ensureAbility();
if (oa == null) {
continue;
}
CardCollection targets = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
// need to set Activating player
oa.setActivatingPlayer(ai);
CardCollection targets = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), oa);
if (!targets.isEmpty()){
boolean canSurvive = false;
for (Card humanCreature : targets) {
if (!FightAi.canKill(humanCreature, source, 0)){
canSurvive = true;
if (!targets.isEmpty()){
boolean canSurvive = false;
for (Card humanCreature : targets) {
if (!FightAi.canKill(humanCreature, source, 0)){
canSurvive = true;
break;
}
}
}
if (!canSurvive){
return false;
}
if (!canSurvive){
return false;
}
};
found = true;
break;
}
if (!found) {
return false;
}
}
@@ -455,7 +477,6 @@ public class CountersPutAi extends SpellAbilityAi {
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
// don't put the counter on the dead creature
if (sacSelf && c.equals(source)) {
return false;
@@ -468,6 +489,8 @@ public class CountersPutAi extends SpellAbilityAi {
Card sacTarget = ComputerUtil.getCardPreference(ai, source, "SacCost", list);
// this card is planned to be sacrificed during cost payment, so don't target it
// (otherwise the AI can cheat by activating this SA and not paying the sac cost, e.g. Extruder)
// TODO needs update if amount > 1 gets printed,
// maybe also check putting the counter on that exact creature is more important than sacrificing it (though unlikely?)
list.remove(sacTarget);
}
@@ -592,7 +615,7 @@ public class CountersPutAi extends SpellAbilityAi {
// Instant +1/+1
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) {
if (!(ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN) && abCost.isReusuableResource())) {
return false; // only if next turn and cost is reusable
return false; // only if next turn and cost is reusable
}
}
}
@@ -841,17 +864,16 @@ public class CountersPutAi extends SpellAbilityAi {
choice = Aggregates.random(list);
}
}
if (choice != null && divided) {
int alloc = Math.max(amount / totalTargets, 1);
if (sa.getTargets().size() == Math.min(totalTargets, sa.getMaxTargets()) - 1) {
sa.addDividedAllocation(choice, left);
} else {
sa.addDividedAllocation(choice, alloc);
left -= alloc;
}
}
if (choice != null && divided) {
int alloc = Math.max(amount / totalTargets, 1);
if (sa.getTargets().size() == Math.min(totalTargets, sa.getMaxTargets()) - 1) {
sa.addDividedAllocation(choice, left);
} else {
sa.addDividedAllocation(choice, alloc);
left -= alloc;
}
}
if (choice != null) {
sa.getTargets().add(choice);
list.remove(choice);

View File

@@ -195,7 +195,6 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
*/
@Override
public CounterType chooseCounterType(List<CounterType> options, SpellAbility sa, Map<String, Object> params) {
if (options.size() > 1) {
final Player ai = sa.getActivatingPlayer();
final Game game = ai.getGame();

View File

@@ -81,7 +81,6 @@ public class CountersRemoveAi extends SpellAbilityAi {
*/
@Override
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
final String type = sa.getParam("CounterType");
if (sa.usesTargeting()) {

View File

@@ -14,7 +14,7 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerDamageDone;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
@@ -56,7 +56,7 @@ public abstract class DamageAiBase extends SpellAbilityAi {
boolean dmgByCardsInHand = false;
if ("X".equals(sa.getParam("NumDmg")) && sa.getHostCard() != null && sa.hasSVar(sa.getParam("NumDmg")) &&
sa.getHostCard().getSVar(sa.getParam("NumDmg")).equals("TargetedPlayer$CardsInHand")) {
sa.getSVar(sa.getParam("NumDmg")).equals("TargetedPlayer$CardsInHand")) {
dmgByCardsInHand = true;
}
// Not sure if type choice implemented for the AI yet but it should at least recognize this spell hits harder on larger enemy hand size
@@ -75,7 +75,7 @@ public abstract class DamageAiBase extends SpellAbilityAi {
// If has triggered ability on dealing damage to an opponent, go for it!
Card hostcard = sa.getHostCard();
for (Trigger trig : hostcard.getTriggers()) {
if (trig instanceof TriggerDamageDone) {
if (trig.getMode() == TriggerType.DamageDone) {
if (("Opponent".equals(trig.getParam("ValidTarget")))
&& (!"True".equals(trig.getParam("CombatDamage")))) {
return true;
@@ -111,8 +111,7 @@ public abstract class DamageAiBase extends SpellAbilityAi {
final CardCollectionView hand = comp.getCardsIn(ZoneType.Hand);
if ((enemy.getLife() - restDamage) < 5) {
// drop the human to less than 5
// life
// drop the human to less than 5 life
return true;
}
@@ -147,12 +146,12 @@ public abstract class DamageAiBase extends SpellAbilityAi {
}
}
}
if (value > 0) { //more likely to burn with larger hand
if (value > 0) { //more likely to burn with larger hand
for (int i = 3; i < hand.size(); i++) {
value *= 1.1f;
}
}
if (value < 0.2f) { //hard floor to reduce ridiculous odds for instants over time
if (value < 0.2f) { //hard floor to reduce ridiculous odds for instants over time
return false;
} else {
final float chance = MyRandom.getRandom().nextFloat();

View File

@@ -46,7 +46,7 @@ public class DamageAllAi extends SpellAbilityAi {
int x = -1;
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
if (damage.equals("X") && sa.getSVar(damage).equals("Count$Converge")) {
dmg = ComputerUtilMana.getConvergeCount(sa, ai);
}
@@ -202,7 +202,7 @@ public class DamageAllAi extends SpellAbilityAi {
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
sa.setXManaCostPaid(dmg);
} else {
dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
dmg = AbilityUtils.calculateAmount(source, damage, sa);
}
if (sa.hasParam("ValidPlayers")) {
@@ -286,7 +286,7 @@ public class DamageAllAi extends SpellAbilityAi {
dmg = ComputerUtilCost.getMaxXValue(sa, ai);
sa.setXManaCostPaid(dmg);
} else {
dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
dmg = AbilityUtils.calculateAmount(source, damage, sa);
}
if (sa.hasParam("ValidPlayers")) {

View File

@@ -30,22 +30,21 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.cost.Cost;
import forge.game.cost.CostPartMana;
import forge.game.cost.CostRemoveCounter;
import forge.game.keyword.Keyword;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerPredicates;
import forge.game.player.PlayerCollection;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetChoices;
import forge.game.spellability.TargetRestrictions;
import forge.game.staticability.StaticAbilityMustTarget;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.MyRandom;
@@ -54,10 +53,9 @@ public class DamageDealAi extends DamageAiBase {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
final String logic = sa.getParam("AILogic");
Card source = sa.getHostCard();
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
final String logic = sa.getParam("AILogic");
if ("MadSarkhanDigDmg".equals(logic)) {
return SpecialCardAi.SarkhanTheMad.considerDig(ai, sa);
@@ -102,13 +100,12 @@ public class DamageDealAi extends DamageAiBase {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Cost abCost = sa.getPayCosts();
final Card source = sa.getHostCard();
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
if (damage.equals("X")) {
if (sa.getSVar(damage).equals("Count$xPaid") || sourceName.equals("Crater's Claws")) {
@@ -131,7 +128,7 @@ public class DamageDealAi extends DamageAiBase {
// Set PayX here to maximum value. It will be adjusted later depending on the target.
sa.setXManaCostPaid(dmg);
} else if (sa.getSVar(damage).contains("InYourHand") && source.isInZone(ZoneType.Hand)) {
dmg = CardFactoryUtil.xCount(source, sa.getSVar(damage)) - 1; // the card will be spent casting the spell, so actual damage is 1 less
dmg = AbilityUtils.calculateAmount(source, damage, sa) - 1; // the card will be spent casting the spell, so actual damage is 1 less
} else if (sa.getSVar(damage).equals("TargetedPlayer$CardsInHand")) {
// cards that deal damage by the number of cards in target player's hand, e.g. Sudden Impact
if (sa.getTargetRestrictions().canTgtPlayer()) {
@@ -343,6 +340,9 @@ public class DamageDealAi extends DamageAiBase {
final Game game = source.getGame();
List<Card> hPlay = getTargetableCards(ai, sa, pl, tgt, activator, source, game);
// Filter MustTarget requirements
StaticAbilityMustTarget.filterMustTargetCards(ai, hPlay, sa);
CardCollection killables = CardLists.filter(hPlay, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -437,63 +437,12 @@ public class DamageDealAi extends DamageAiBase {
// We can hurt a planeswalker, so rank the one which is the best target
if (!hPlay.isEmpty() && pl.isOpponentOf(ai) && activator.equals(ai)) {
return getBestPlaneswalkerToDamage(hPlay);
return ComputerUtilCard.getBestPlaneswalkerToDamage(hPlay);
}
return null;
}
private Card getBestPlaneswalkerToDamage(final List<Card> pws) {
Card bestTgt = null;
// 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(CounterEnumType.LOYALTY);
int pwScore = curLoyalty * 10;
for (SpellAbility sa : pw.getSpellAbilities()) {
if (sa.hasParam("Ultimate")) {
Integer loyaltyCost = 0;
CostRemoveCounter remLoyalty = sa.getPayCosts().getCostPartByType(CostRemoveCounter.class);
if (remLoyalty != null) {
// if remLoyalty is null, generally there's an AddCounter<0/LOYALTY> cost, like for Gideon Jura.
loyaltyCost = remLoyalty.convertAmount();
}
if (loyaltyCost != null && loyaltyCost != 0 && loyaltyCost - curLoyalty <= 1) {
// Will ultimate soon
pwScore += 10000;
}
if (pwScore > bestScore) {
bestScore = pwScore;
bestTgt = pw;
}
}
}
}
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);
@@ -582,7 +531,7 @@ public class DamageDealAi extends DamageAiBase {
&& "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);
final int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
dmg += amount;
}
@@ -730,12 +679,10 @@ public class DamageDealAi extends DamageAiBase {
}
// When giving priority to targeting Creatures for mandatory
// triggers
// feel free to add the Human after we run out of good targets
// triggers feel free to add the Human after we run out of good targets
// TODO: add check here if card is about to die from something
// on the stack
// or from taking combat damage
// on the stack or from taking combat damage
final Cost abCost = sa.getPayCosts();
boolean freePing = immediately || abCost == null
@@ -792,8 +739,7 @@ public class DamageDealAi extends DamageAiBase {
}
}
}
// TODO: Improve Damage, we shouldn't just target the player just
// because we can
// TODO: Improve Damage, we shouldn't just target the player just because we can
if (sa.canTarget(enemy) && tcs.size() < tgt.getMaxTargets(source, sa)) {
if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai))
|| (SpellAbilityAi.isSorcerySpeed(sa) && phase.is(PhaseType.MAIN2))
@@ -898,7 +844,7 @@ public class DamageDealAi extends DamageAiBase {
// this is for Triggered targets that are mandatory
final boolean noPrevention = sa.hasParam("NoPrevention");
final boolean divided = sa.isDividedAsYouChoose();
final Player opp = ai.getWeakestOpponent();
PlayerCollection opps = ai.getOpponents();
while (sa.canAddMoreTarget()) {
if (tgt.canTgtPlaneswalker()) {
@@ -926,13 +872,17 @@ public class DamageDealAi extends DamageAiBase {
}
}
if (sa.canTarget(opp)) {
if (sa.getTargets().add(opp)) {
if (divided) {
sa.addDividedAllocation(opp, dmg);
break;
if (!opps.isEmpty()) {
Player opp = opps.getFirst();
opps.remove(opp);
if (sa.canTarget(opp)) {
if (sa.getTargets().add(opp)) {
if (divided) {
sa.addDividedAllocation(opp, dmg);
break;
}
continue;
}
continue;
}
}
@@ -951,7 +901,7 @@ public class DamageDealAi extends DamageAiBase {
}
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)));
final Card c = ComputerUtilCard.getWorstPlaneswalkerToDamage(CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), Predicates.and(CardPredicates.Presets.PLANESWALKERS), CardPredicates.isTargetableBy(sa)));
if (c != null) {
sa.getTargets().add(c);
if (divided) {
@@ -981,7 +931,6 @@ public class DamageDealAi extends DamageAiBase {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getHostCard();
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(source, damage, sa);
@@ -1038,7 +987,7 @@ public class DamageDealAi extends DamageAiBase {
saTgt = saTgt.getParent();
}
Player opponent = ai.getOpponents().min(PlayerPredicates.compareByLife());
Player opponent = ai.getWeakestOpponent();
// TODO: somehow account for the possible cost reduction?
int dmg = ComputerUtilMana.determineLeftoverMana(sa, ai, saTgt.getParam("XColor"));
@@ -1081,7 +1030,7 @@ public class DamageDealAi extends DamageAiBase {
saTgt.resetTargets();
saTgt.getTargets().add(tgtCreature != null && dmg < opponent.getLife() ? tgtCreature : opponent);
sa.setXManaCostPaid(dmg);
saTgt.setXManaCostPaid(dmg);
return true;
}

View File

@@ -8,6 +8,7 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
@@ -26,9 +27,6 @@ import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
public class DebuffAi extends SpellAbilityAi {
// *************************************************************************
// ***************************** Debuff ************************************
// *************************************************************************
@Override
protected boolean canPlayAI(final Player ai, final SpellAbility sa) {
@@ -140,7 +138,6 @@ public class DebuffAi extends SpellAbilityAi {
while (sa.getTargets().size() < tgt.getMaxTargets(sa.getHostCard(), sa)) {
Card t = null;
// boolean goodt = false;
if (list.isEmpty()) {
if ((sa.getTargets().size() < tgt.getMinTargets(sa.getHostCard(), sa)) || (sa.getTargets().size() == 0)) {
@@ -176,19 +173,18 @@ public class DebuffAi extends SpellAbilityAi {
* @return a CardCollection.
*/
private CardCollection getCurseCreatures(final Player ai, final SpellAbility sa, final List<String> kws) {
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
CardCollection list = CardLists.getTargetableCards(opp.getCreaturesInPlay(), sa);
if (!list.isEmpty()) {
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.hasAnyKeyword(kws); // don't add duplicate negative
// keywords
return c.hasAnyKeyword(kws); // don't add duplicate negative keywords
}
});
}
return list;
} // getCurseCreatures()
}
/**
* <p>
@@ -216,7 +212,7 @@ public class DebuffAi extends SpellAbilityAi {
list.remove(c);
}
final CardCollection pref = CardLists.filterControlledBy(list, ai.getWeakestOpponent());
final CardCollection pref = CardLists.filterControlledBy(list, ai.getOpponents());
final CardCollection forced = CardLists.filterControlledBy(list, ai);
final Card source = sa.getHostCard();
@@ -242,8 +238,7 @@ public class DebuffAi extends SpellAbilityAi {
break;
}
// TODO - if forced targeting, just pick something without the given
// keyword
// TODO - if forced targeting, just pick something without the given keyword
Card c;
if (CardLists.getNotType(forced, "Creature").size() == 0) {
c = ComputerUtilCard.getWorstCreatureAI(forced);

View File

@@ -2,15 +2,7 @@ package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.AiController;
import forge.ai.AiProps;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.PlayerControllerAi;
import forge.ai.SpecialAiLogic;
import forge.ai.SpecialCardAi;
import forge.ai.SpellAbilityAi;
import forge.ai.*;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
@@ -26,12 +18,13 @@ import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbilityMustTarget;
import forge.game.zone.ZoneType;
public class DestroyAi extends SpellAbilityAi {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
return canPlayAI(ai, sa);
return checkApiLogic(ai, sa);
}
@Override
@@ -122,17 +115,21 @@ public class DestroyAi extends SpellAbilityAi {
CardCollection list;
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false;
}
// Targeting
if (sa.usesTargeting()) {
// If there's X in payment costs and it's tied to targeting, make sure we set the XManaCostPaid first
// (e.g. Heliod's Intervention)
if ("X".equals(sa.getTargetRestrictions().getMinTargets()) && sa.getSVar("X").equals("Count$xPaid")) {
int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
sa.getRootAbility().setXManaCostPaid(xPay);
}
// Assume there where already enough targets chosen by AI Logic Above
if (!sa.canAddMoreTarget() && sa.isTargetNumberValid()) {
if (sa.hasParam("AILogic") && !sa.canAddMoreTarget() && sa.isTargetNumberValid()) {
return true;
}
@@ -140,11 +137,11 @@ public class DestroyAi extends SpellAbilityAi {
sa.resetTargets();
int maxTargets;
if (sa.costHasManaX()) {
if (sa.getRootAbility().costHasManaX()) {
// TODO: currently the AI will maximize mana spent on X, trying to maximize damage. This may need improvement.
maxTargets = ComputerUtilCost.getMaxXValue(sa, ai);
// need to set XPaid to get the right number for
sa.setXManaCostPaid(maxTargets);
sa.getRootAbility().setXManaCostPaid(maxTargets);
// need to check for maxTargets
maxTargets = Math.min(maxTargets, sa.getMaxTargets());
} else {
@@ -226,6 +223,10 @@ public class DestroyAi extends SpellAbilityAi {
// target loop
// TODO use can add more Targets
while (sa.getTargets().size() < maxTargets) {
// filter by MustTarget requirement
CardCollection originalList = new CardCollection(list);
boolean mustTargetFiltered = StaticAbilityMustTarget.filterMustTargetCards(ai, list, sa);
if (list.isEmpty()) {
if (!sa.isMinTargetChosen() || sa.isZeroTargets()) {
sa.resetTargets();
@@ -286,6 +287,12 @@ public class DestroyAi extends SpellAbilityAi {
}
}
}
// Restore original list for next loop if filtered by MustTarget requirement
if (mustTargetFiltered) {
list = originalList;
}
list.remove(choice);
sa.getTargets().add(choice);
}

View File

@@ -70,12 +70,12 @@ public class DestroyAllAi extends SpellAbilityAi {
return doMassRemovalLogic(ai, sa);
}
public boolean doMassRemovalLogic(Player ai, SpellAbility sa) {
public static boolean doMassRemovalLogic(Player ai, SpellAbility sa) {
final Card source = sa.getHostCard();
final String logic = sa.getParamOrDefault("AILogic", "");
Player opponent = ai.getWeakestOpponent(); // TODO: how should this AI logic work for multiplayer and getOpponents()?
final int CREATURE_EVAL_THRESHOLD = 200;
// if we hit the whole board, the other opponents who are not the reason to cast this probably still suffer a bit too
final int CREATURE_EVAL_THRESHOLD = 200 / (!sa.usesTargeting() ? ai.getOpponents().size() : 1);
if (logic.equals("Always")) {
return true; // e.g. Tetzimoc, Primal Death, where we want to cast the permanent even if the removal trigger does nothing
@@ -93,99 +93,101 @@ public class DestroyAllAi extends SpellAbilityAi {
valid = valid.replace("X", Integer.toString(xPay));
}
CardCollection opplist = CardLists.getValidCards(opponent.getCardsIn(ZoneType.Battlefield),
valid.split(","), source.getController(), source, sa);
CardCollection ailist = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","),
source.getController(), source, sa);
// TODO should probably sort results when targeted to use on biggest threat instead of first match
for (Player opponent: ai.getOpponents()) {
CardCollection opplist = CardLists.getValidCards(opponent.getCardsIn(ZoneType.Battlefield), valid.split(","), source.getController(), source, sa);
CardCollection ailist = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), source.getController(), source, sa);
opplist = CardLists.filter(opplist, predicate);
ailist = CardLists.filter(ailist, predicate);
if (opplist.isEmpty()) {
return false;
}
if (sa.usesTargeting()) {
sa.resetTargets();
if (sa.canTarget(opponent)) {
sa.getTargets().add(opponent);
ailist.clear();
} else {
opplist = CardLists.filter(opplist, predicate);
ailist = CardLists.filter(ailist, predicate);
if (opplist.isEmpty()) {
return false;
}
}
// Special handling for Raiding Party
if (logic.equals("RaidingParty")) {
int numAiCanSave = Math.min(CardLists.filter(ai.getCreaturesInPlay(), Predicates.and(CardPredicates.isColor(MagicColor.WHITE), CardPredicates.Presets.UNTAPPED)).size() * 2, ailist.size());
int numOppsCanSave = Math.min(CardLists.filter(ai.getOpponents().getCreaturesInPlay(), Predicates.and(CardPredicates.isColor(MagicColor.WHITE), CardPredicates.Presets.UNTAPPED)).size() * 2, opplist.size());
return numOppsCanSave < opplist.size() && (ailist.size() - numAiCanSave < opplist.size() - numOppsCanSave);
}
// If effect is destroying creatures and AI is about to lose, activate effect anyway no matter what!
if ((!CardLists.getType(opplist, "Creature").isEmpty()) && (ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS))
&& (ai.getGame().getCombat() != null && ComputerUtilCombat.lifeInSeriousDanger(ai, ai.getGame().getCombat()))) {
return true;
}
// If effect is destroying creatures and AI is about to get low on life, activate effect anyway if difference in lost permanents not very much
if ((!CardLists.getType(opplist, "Creature").isEmpty()) && (ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS))
&& (ai.getGame().getCombat() != null && ComputerUtilCombat.lifeInDanger(ai, ai.getGame().getCombat()))
&& ((ComputerUtilCard.evaluatePermanentList(ailist) - 6) >= ComputerUtilCard.evaluatePermanentList(opplist))) {
return true;
}
// if only creatures are affected evaluate both lists and pass only if
// human creatures are more valuable
if (CardLists.getNotType(opplist, "Creature").isEmpty() && CardLists.getNotType(ailist, "Creature").isEmpty()) {
if (ComputerUtilCard.evaluateCreatureList(ailist) + CREATURE_EVAL_THRESHOLD < ComputerUtilCard.evaluateCreatureList(opplist)) {
return true;
}
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
return false;
}
// test whether the human can kill the ai next turn
Combat combat = new Combat(opponent);
boolean containsAttacker = false;
for (Card att : opponent.getCreaturesInPlay()) {
if (ComputerUtilCombat.canAttackNextTurn(att, ai)) {
combat.addAttacker(att, ai);
containsAttacker = containsAttacker | opplist.contains(att);
}
}
if (!containsAttacker) {
return false;
}
AiBlockController block = new AiBlockController(ai);
block.assignBlockersForCombat(combat);
if (ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
return true;
}
return false;
} // only lands involved
else if (CardLists.getNotType(opplist, "Land").isEmpty() && CardLists.getNotType(ailist, "Land").isEmpty()) {
if (ai.isCardInPlay("Crucible of Worlds") && !opponent.isCardInPlay("Crucible of Worlds") && !opplist.isEmpty()) {
return true;
}
// evaluate the situation with creatures on the battlefield separately, as that's where the AI typically makes mistakes
CardCollection aiCreatures = ai.getCreaturesInPlay();
CardCollection oppCreatures = opponent.getCreaturesInPlay();
if (!oppCreatures.isEmpty()) {
if (ComputerUtilCard.evaluateCreatureList(aiCreatures) < ComputerUtilCard.evaluateCreatureList(oppCreatures) + CREATURE_EVAL_THRESHOLD) {
if (sa.usesTargeting()) {
sa.resetTargets();
if (sa.canTarget(opponent)) {
sa.getTargets().add(opponent);
ailist.clear();
} else {
return false;
}
}
// check if the AI would lose more lands than the opponent would
if (ComputerUtilCard.evaluatePermanentList(ailist) > ComputerUtilCard.evaluatePermanentList(opplist) + 1) {
// Special handling for Raiding Party
if (logic.equals("RaidingParty")) {
int numAiCanSave = Math.min(CardLists.filter(ai.getCreaturesInPlay(), Predicates.and(CardPredicates.isColor(MagicColor.WHITE), CardPredicates.Presets.UNTAPPED)).size() * 2, ailist.size());
int numOppsCanSave = Math.min(CardLists.filter(ai.getOpponents().getCreaturesInPlay(), Predicates.and(CardPredicates.isColor(MagicColor.WHITE), CardPredicates.Presets.UNTAPPED)).size() * 2, opplist.size());
return numOppsCanSave < opplist.size() && (ailist.size() - numAiCanSave < opplist.size() - numOppsCanSave);
}
// If effect is destroying creatures and AI is about to lose, activate effect anyway no matter what!
if ((!CardLists.getType(opplist, "Creature").isEmpty()) && (ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS))
&& (ai.getGame().getCombat() != null && ComputerUtilCombat.lifeInSeriousDanger(ai, ai.getGame().getCombat()))) {
return true;
}
// If effect is destroying creatures and AI is about to get low on life, activate effect anyway if difference in lost permanents not very much
if ((!CardLists.getType(opplist, "Creature").isEmpty()) && (ai.getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS))
&& (ai.getGame().getCombat() != null && ComputerUtilCombat.lifeInDanger(ai, ai.getGame().getCombat()))
&& ((ComputerUtilCard.evaluatePermanentList(ailist) - 6) >= ComputerUtilCard.evaluatePermanentList(opplist))) {
return true;
}
// if only creatures are affected evaluate both lists and pass only if human creatures are more valuable
if (CardLists.getNotType(opplist, "Creature").isEmpty() && CardLists.getNotType(ailist, "Creature").isEmpty()) {
if (ComputerUtilCard.evaluateCreatureList(ailist) + CREATURE_EVAL_THRESHOLD < ComputerUtilCard.evaluateCreatureList(opplist)) {
return true;
}
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
return false;
}
// test whether the human can kill the ai next turn
Combat combat = new Combat(opponent);
boolean containsAttacker = false;
for (Card att : opponent.getCreaturesInPlay()) {
if (ComputerUtilCombat.canAttackNextTurn(att, ai)) {
combat.addAttacker(att, ai);
containsAttacker = containsAttacker | opplist.contains(att);
}
}
if (!containsAttacker) {
return false;
}
AiBlockController block = new AiBlockController(ai);
block.assignBlockersForCombat(combat);
if (ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
return true;
}
return false;
} // only lands involved
else if (CardLists.getNotType(opplist, "Land").isEmpty() && CardLists.getNotType(ailist, "Land").isEmpty()) {
if (ai.isCardInPlay("Crucible of Worlds") && !opponent.isCardInPlay("Crucible of Worlds")) {
return true;
}
// evaluate the situation with creatures on the battlefield separately, as that's where the AI typically makes mistakes
CardCollection aiCreatures = ai.getCreaturesInPlay();
CardCollection oppCreatures = opponent.getCreaturesInPlay();
if (!oppCreatures.isEmpty()) {
if (ComputerUtilCard.evaluateCreatureList(aiCreatures) < ComputerUtilCard.evaluateCreatureList(oppCreatures) + CREATURE_EVAL_THRESHOLD) {
return false;
}
}
// check if the AI would lose more lands than the opponent would
if (ComputerUtilCard.evaluatePermanentList(ailist) > ComputerUtilCard.evaluatePermanentList(opplist) + 1) {
return false;
}
} // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable
else if ((ComputerUtilCard.evaluatePermanentList(ailist) + 3) >= ComputerUtilCard.evaluatePermanentList(opplist)) {
return false;
}
} // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable
else if ((ComputerUtilCard.evaluatePermanentList(ailist) + 3) >= ComputerUtilCard.evaluatePermanentList(opplist)) {
return false;
return true;
}
return true;
return false;
}
}

View File

@@ -5,6 +5,7 @@ import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard;
@@ -32,7 +33,7 @@ public class DigAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Game game = ai.getGame();
Player opp = ai.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
final Card host = sa.getHostCard();
Player libraryOwner = ai;
@@ -44,9 +45,8 @@ public class DigAi extends SpellAbilityAi {
sa.resetTargets();
if (!opp.canBeTargetedBy(sa)) {
return false;
} else {
sa.getTargets().add(opp);
}
sa.getTargets().add(opp);
libraryOwner = opp;
}
@@ -120,7 +120,7 @@ public class DigAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final SpellAbility root = sa.getRootAbility();
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (sa.usesTargeting()) {
sa.resetTargets();
if (mandatory && sa.canTarget(opp)) {

View File

@@ -1,5 +1,6 @@
package forge.ai.ability;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
@@ -19,7 +20,7 @@ public class DigMultipleAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Game game = ai.getGame();
Player opp = ai.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
final Card host = sa.getHostCard();
Player libraryOwner = ai;
@@ -27,9 +28,8 @@ public class DigMultipleAi extends SpellAbilityAi {
sa.resetTargets();
if (!opp.canBeTargetedBy(sa)) {
return false;
} else {
sa.getTargets().add(opp);
}
sa.getTargets().add(opp);
libraryOwner = opp;
}
@@ -77,7 +77,7 @@ public class DigMultipleAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (sa.usesTargeting()) {
sa.resetTargets();
if (mandatory && sa.canTarget(opp)) {

View File

@@ -2,6 +2,7 @@ package forge.ai.ability;
import java.util.List;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
@@ -31,10 +32,8 @@ public class DigUntilAi extends SpellAbilityAi {
chance = 1;
}
final boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
Player libraryOwner = ai;
Player opp = ai.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if ("DontMillSelf".equals(logic)) {
// A card that digs for specific things and puts everything revealed before it into graveyard
@@ -61,9 +60,8 @@ public class DigUntilAi extends SpellAbilityAi {
sa.resetTargets();
if (!sa.canTarget(opp)) {
return false;
} else {
sa.getTargets().add(opp);
}
sa.getTargets().add(opp);
libraryOwner = opp;
} else {
if (sa.hasParam("Valid")) {
@@ -92,12 +90,12 @@ public class DigUntilAi extends SpellAbilityAi {
return false;
}
final boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);
return randomReturn;
}
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) {
sa.resetTargets();
if (sa.isCurse()) {

View File

@@ -2,6 +2,7 @@ package forge.ai.ability;
import java.util.List;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCost;
@@ -60,11 +61,9 @@ public class DiscardAi extends SpellAbilityAi {
if (players.get(0) == ai) {
// the ai should only be using something like this if he has
// few cards in hand,
// cards like this better have a good drawback to be in the
// AIs deck
// cards like this better have a good drawback to be in the AIs deck
} else {
// defined to the human, so that's fine as long the human
// has cards
// defined to the human, so that's fine as long the human has cards
if (!humanHasHand) {
return false;
}
@@ -170,7 +169,7 @@ public class DiscardAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
Player opp = ai.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (!discardTargetAI(ai, sa)) {
if (mandatory && sa.canTarget(opp)) {
sa.getTargets().add(opp);

View File

@@ -23,8 +23,7 @@ public class DrainManaAi extends SpellAbilityAi {
if (tgt == null) {
// assume we are looking to tap human's stuff
// TODO - check for things with untap abilities, and don't tap
// those.
// TODO - check for things with untap abilities, and don't tap those.
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
if (!defined.contains(opp)) {

View File

@@ -244,7 +244,7 @@ public class DrawAi extends SpellAbilityAi {
int numCards = 1;
if (sa.hasParam("NumCards")) {
numCards = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumCards"), sa);
numCards = AbilityUtils.calculateAmount(source, sa.getParam("NumCards"), sa);
}
boolean xPaid = false;
@@ -257,7 +257,7 @@ public class DrawAi extends SpellAbilityAi {
} else {
numCards = ComputerUtilCost.getMaxXValue(sa, ai);
// try not to overdraw
int safeDraw = Math.min(computerMaxHandSize - computerHandSize, computerLibrarySize - 3);
int safeDraw = Math.abs(Math.min(computerMaxHandSize - computerHandSize, computerLibrarySize - 3));
if (sa.getHostCard().isInstant() || sa.getHostCard().isSorcery()) { safeDraw++; } // card will be spent
numCards = Math.min(numCards, safeDraw);
@@ -377,7 +377,7 @@ public class DrawAi extends SpellAbilityAi {
// checks what the ai prevent from casting it on itself
// if spell is not mandatory
if (aiTarget && !ai.cantLose()) {
if (numCards >= computerLibrarySize) {
if (numCards >= computerLibrarySize - 3) {
if (xPaid) {
numCards = computerLibrarySize - 1;
if (numCards <= 0 && !mandatory) {
@@ -422,8 +422,7 @@ public class DrawAi extends SpellAbilityAi {
}
root.setXManaCostPaid(numCards);
} else {
// Don't draw too many cards and then risk discarding
// cards at EOT
// Don't draw too many cards and then risk discarding cards at EOT
if (!drawback && !mandatory) {
return false;
}
@@ -441,7 +440,7 @@ public class DrawAi extends SpellAbilityAi {
continue;
}
// use xPaid abilties only for itself
// use xPaid abilities only for itself
if (xPaid) {
continue;
}
@@ -493,7 +492,7 @@ public class DrawAi extends SpellAbilityAi {
// TODO: consider if human is the defined player
// ability is not targeted
if (numCards >= computerLibrarySize) {
if (numCards >= computerLibrarySize - 3) {
if (ai.isCardInPlay("Laboratory Maniac")) {
return true;
}
@@ -509,8 +508,7 @@ public class DrawAi extends SpellAbilityAi {
&& game.getPhaseHandler().isPlayerTurn(ai)
&& !sa.isTrigger()
&& !assumeSafeX) {
// Don't draw too many cards and then risk discarding cards at
// EOT
// Don't draw too many cards and then risk discarding cards at EOT
if (!drawback) {
return false;
}

View File

@@ -79,7 +79,7 @@ public class EffectAi extends SpellAbilityAi {
if (!game.getStack().isEmpty()) {
return false;
}
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
if (game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
return false;
}
if (!ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {

View File

@@ -19,6 +19,7 @@ import forge.game.keyword.Keyword;
import forge.game.player.Player;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbilityMustTarget;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.util.MyRandom;
@@ -50,6 +51,9 @@ public class FightAi extends SpellAbilityAi {
aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
List<Card> humCreatures = ai.getOpponents().getCreaturesInPlay();
humCreatures = CardLists.getTargetableCards(humCreatures, sa);
// Filter MustTarget requirements
StaticAbilityMustTarget.filterMustTargetCards(ai, humCreatures, sa);
if (humCreatures.isEmpty())
return false; //prevent IndexOutOfBoundsException on MOJHOSTO variant
@@ -63,8 +67,7 @@ public class FightAi extends SpellAbilityAi {
for (Card humanCreature : humCreatures) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= fighter1.getNetPower()
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(fighter1)) {
// todo: check min/max targets; see if we picked the best
// matchup
// todo: check min/max targets; see if we picked the best matchup
sa.getTargets().add(humanCreature);
return true;
} else if (humanCreature.getSVar("Targeting").equals("Dies")) {
@@ -81,8 +84,7 @@ public class FightAi extends SpellAbilityAi {
for (Card aiCreature : aiCreatures) {
if (ComputerUtilCombat.getDamageToKill(humanCreature) <= aiCreature.getNetPower()
&& humanCreature.getNetPower() < ComputerUtilCombat.getDamageToKill(aiCreature)) {
// todo: check min/max targets; see if we picked the
// best matchup
// todo: check min/max targets; see if we picked the best matchup
sa.getTargets().add(humanCreature);
sa.getTargets().add(aiCreature);
return true;

View File

@@ -16,14 +16,32 @@ public class FlipACoinAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) {
if (sa.hasParam("AILogic")) {
String AILogic = sa.getParam("AILogic");
if (AILogic.equals("Never")) {
String ailogic = sa.getParam("AILogic");
if (ailogic.equals("Never")) {
return false;
} else if (AILogic.equals("PhaseOut")) {
} else if (ailogic.equals("PhaseOut")) {
if (!ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(sa.getHostCard())) {
return false;
}
} else if (AILogic.equals("KillOrcs")) {
} else if (ailogic.equals("Bangchuckers")) {
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN) ) {
return false;
}
sa.resetTargets();
for (Player o : ai.getOpponents()) {
if (sa.canTarget(o) && o.canLoseLife() && !o.cantLose()) {
sa.getTargets().add(o);
return true;
}
}
for (Card c : ai.getOpponents().getCreaturesInPlay()) {
if (sa.canTarget(c)) {
sa.getTargets().add(c);
return true;
}
}
return false;
} else if (ailogic.equals("KillOrcs")) {
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN) ) {
return false;
}

View File

@@ -29,7 +29,7 @@ public class FogAi extends SpellAbilityAi {
final Card hostCard = sa.getHostCard();
// Don't cast it, if the effect is already in place
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
if (game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
return false;
}

View File

@@ -13,8 +13,7 @@ public class GameLossAi extends SpellAbilityAi {
return false;
}
// Only one SA Lose the Game card right now, which is Door to
// Nothingness
// Only one SA Lose the Game card right now, which is Door to Nothingness
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
@@ -29,20 +28,23 @@ public class GameLossAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
Player loser = ai;
// Phage the Untouchable
// (Final Fortune would need to attach it's delayed trigger to a
// specific turn, which can't be done yet)
Player opp = ai.getWeakestOpponent();
if (ai.getGame().getCombat() != null) {
loser = ai.getGame().getCombat().getDefenderPlayerByAttacker(sa.getHostCard());
}
if (!mandatory && opp.cantLose()) {
if (!mandatory && loser.cantLose()) {
return false;
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
sa.resetTargets();
sa.getTargets().add(opp);
sa.getTargets().add(loser);
}
return true;

View File

@@ -0,0 +1,56 @@
package forge.ai.ability;
import forge.ai.ComputerUtilCard;
import forge.ai.PlayerControllerAi;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
public class LearnAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
// For the time being, Learn is treated as universally positive due to being optional
return true;
}
@Override
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
return mandatory || canPlayAI(aiPlayer, sa);
}
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
return canPlayAI(aiPlayer, sa);
}
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return true;
}
public static Card chooseCardToLearn(CardCollection options, Player ai, SpellAbility sa) {
CardCollection sideboard = CardLists.filter(options, CardPredicates.inZone(ZoneType.Sideboard));
CardCollection hand = CardLists.filter(options, CardPredicates.inZone(ZoneType.Hand));
hand.remove(sa.getHostCard()); // this card will be used in the process, don't consider it for discard
CardCollection lessons = CardLists.filter(sideboard, CardPredicates.isType("Lesson"));
CardCollection goodDiscards = ((PlayerControllerAi)ai.getController()).getAi().getCardsToDiscard(1, 1, hand, sa);
if (!lessons.isEmpty()) {
return ComputerUtilCard.getBestAI(lessons);
} else if (!goodDiscards.isEmpty()) {
return ComputerUtilCard.getWorstAI(goodDiscards);
}
// Don't choose anything if there's no good option
return null;
}
}

View File

@@ -1,5 +1,6 @@
package forge.ai.ability;
import forge.ai.AiAttackController;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -19,7 +20,7 @@ public class LifeExchangeAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
final int myLife = aiPlayer.getLife();
Player opponent = aiPlayer.getWeakestOpponent();
Player opponent = AiAttackController.choosePreferredDefenderPlayer(aiPlayer);
final int hLife = opponent.getLife();
if (!aiPlayer.canGainLife()) {
@@ -75,7 +76,7 @@ public class LifeExchangeAi extends SpellAbilityAi {
final boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
Player opp = ai.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (tgt != null) {
sa.resetTargets();
if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) {

View File

@@ -1,5 +1,6 @@
package forge.ai.ability;
import forge.ai.AiAttackController;
import forge.ai.AiProps;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilAbility;
@@ -154,7 +155,7 @@ public class LifeExchangeVariantAi extends SpellAbilityAi {
final boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
Player opp = ai.getWeakestOpponent();
Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (tgt != null) {
sa.resetTargets();
if (sa.canTarget(opp) && (mandatory || ai.getLife() < opp.getLife())) {

View File

@@ -266,8 +266,7 @@ public class LifeGainAi extends SpellAbilityAi {
}
if (!hasTgt && mandatory) {
// need to target something but its neither negative against
// opponents,
// nor posive against allies
// opponents, nor positive against allies
// hurting ally is probably better than healing opponent
// look for Lifegain not Negative (case of lifegain negated)
@@ -295,8 +294,7 @@ public class LifeGainAi extends SpellAbilityAi {
sa.getTargets().add(ally);
hasTgt = true;
}
// better heal opponent which most life then the one with the
// lowest
// better heal opponent which most life then the one with the lowest
if (!hasTgt) {
Player opp = opps.max(PlayerPredicates.compareByLife());
sa.getTargets().add(opp);

View File

@@ -28,7 +28,6 @@ public class LifeLoseAi extends SpellAbilityAi {
*/
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
final Card source = sa.getHostCard();
@@ -60,7 +59,6 @@ public class LifeLoseAi extends SpellAbilityAi {
return true;
}
/*
* (non-Javadoc)
*
@@ -123,12 +121,12 @@ public class LifeLoseAi extends SpellAbilityAi {
return false;
}
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
if (ComputerUtil.playImmediately(ai, sa)) {
return true;
}
final PlayerCollection tgtPlayers = getPlayers(ai, sa);
// TODO: check against the amount we could obtain when multiple activations are possible
PlayerCollection filteredPlayer = tgtPlayers
.filter(Predicates.and(PlayerPredicates.isOpponentOf(ai), PlayerPredicates.lifeLessOrEqualTo(amount)));
// killing opponents asap

View File

@@ -17,7 +17,7 @@ public class LifeSetAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final int myLife = ai.getLife();
final Player opponent = ai.getWeakestOpponent();
final Player opponent = ai.getStrongestOpponent();
final int hlife = opponent.getLife();
final String amountStr = sa.getParam("LifeAmount");
@@ -36,8 +36,7 @@ public class LifeSetAi extends SpellAbilityAi {
return false;
}
// TODO handle proper calculation of X values based on Cost and what
// would be paid
// TODO handle proper calculation of X values based on Cost and what would be paid
int amount;
// we shouldn't have to worry too much about PayX for SetLife
if (amountStr.equals("X") && sa.getSVar(amountStr).equals("Count$xPaid")) {
@@ -58,11 +57,9 @@ public class LifeSetAi extends SpellAbilityAi {
if (tgt.canOnlyTgtOpponent()) {
sa.getTargets().add(opponent);
// if we can only target the human, and the Human's life
// would
// go up, don't play it.
// would go up, don't play it.
// possibly add a combo here for Magister Sphinx and
// Higedetsu's
// (sp?) Second Rite
// Higedetsu's (sp?) Second Rite
if ((amount > hlife) || !opponent.canLoseLife()) {
return false;
}
@@ -81,8 +78,7 @@ public class LifeSetAi extends SpellAbilityAi {
if (sa.getParam("Defined").equals("Player")) {
if (amount == 0) {
return false;
} else if (myLife > amount) { // will decrease computer's
// life
} else if (myLife > amount) { // will decrease computer's life
if ((myLife < 5) || ((myLife - amount) > (hlife - amount))) {
return false;
}
@@ -104,7 +100,7 @@ public class LifeSetAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final int myLife = ai.getLife();
final Player opponent = ai.getWeakestOpponent();
final Player opponent = ai.getStrongestOpponent();
final int hlife = opponent.getLife();
final Card source = sa.getHostCard();
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
@@ -118,7 +114,7 @@ public class LifeSetAi extends SpellAbilityAi {
sa.setXManaCostPaid(xPay);
amount = xPay;
} else {
amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
}
// special cases when amount can't be calculated without targeting first
@@ -133,8 +129,7 @@ public class LifeSetAi extends SpellAbilityAi {
}
// If the Target is gaining life, target self.
// if the Target is modifying how much life is gained, this needs to
// be handled better
// if the Target is modifying how much life is gained, this needs to be handled better
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
sa.resetTargets();

View File

@@ -21,6 +21,7 @@ import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.player.PlayerPredicates;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
@@ -214,13 +215,7 @@ public class MillAi extends SpellAbilityAi {
}
// get targeted or defined Player with largest library
// TODO in Java 8 find better way
final Player m = Collections.max(list, new Comparator<Player>() {
@Override
public int compare(Player arg0, Player arg1) {
return arg0.getCardsIn(ZoneType.Library).size() - arg1.getCardsIn(ZoneType.Library).size();
}
});
final Player m = Collections.max(list, PlayerPredicates.compareByZoneSize(ZoneType.Library));
int cardsToDiscard = m.getCardsIn(ZoneType.Library).size();

View File

@@ -78,7 +78,7 @@ public class MustBlockAi extends SpellAbilityAi {
Card attacker = null;
if (sa.hasParam("DefinedAttacker")) {
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
final List<Card> cards = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedAttacker"), sa);
if (cards.isEmpty()) {
return false;
}

View File

@@ -33,7 +33,6 @@ public class PermanentAi extends SpellAbilityAi {
*/
@Override
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
final Card card = sa.getHostCard();
if (card.hasKeyword("MayFlashSac") && !ai.couldCastSorcery(sa)) {
@@ -51,7 +50,6 @@ public class PermanentAi extends SpellAbilityAi {
*/
@Override
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
final Card card = sa.getHostCard();
final Game game = ai.getGame();
@@ -297,7 +295,7 @@ public class PermanentAi extends SpellAbilityAi {
if (!checkPhaseRestrictions(ai, sa, ai.getGame().getPhaseHandler())) {
return false;
}
return checkApiLogic(ai, sa);
return checkApiLogic(ai, sa) || mandatory;
}
}

View File

@@ -58,7 +58,6 @@ public class PermanentCreatureAi extends PermanentAi {
*/
@Override
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
final Card card = sa.getHostCard();
final Game game = ai.getGame();
@@ -176,7 +175,6 @@ public class PermanentCreatureAi extends PermanentAi {
}
}
if (hasFloatMana || willDiscardNow || willDieNow) {
// Will lose mana in pool or about to discard a card in cleanup or about to die in combat, so use this opportunity
return true;
@@ -207,7 +205,6 @@ public class PermanentCreatureAi extends PermanentAi {
@Override
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
if (!super.checkApiLogic(ai, sa)) {
return false;
}

View File

@@ -8,7 +8,6 @@ import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
/**
@@ -28,7 +27,6 @@ public class PermanentNoncreatureAi extends PermanentAi {
*/
@Override
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
if (!super.checkApiLogic(ai, sa))
return false;
@@ -40,10 +38,8 @@ public class PermanentNoncreatureAi extends PermanentAi {
if (host.hasSVar("OblivionRing")) {
SpellAbility effectExile = AbilityFactory.getAbility(host.getSVar("TrigExile"), host);
final ZoneType origin = ZoneType.listValueOf(effectExile.getParam("Origin")).get(0);
final TargetRestrictions tgt = effectExile.getTargetRestrictions();
final CardCollection list = CardLists.getValidCards(game.getCardsIn(origin), tgt.getValidTgts(), ai, host,
effectExile);
CardCollection targets = CardLists.getTargetableCards(list, sa);
effectExile.setActivatingPlayer(ai);
CardCollection targets = CardLists.getTargetableCards(game.getCardsIn(origin), effectExile);
if (sourceName.equals("Suspension Field")
|| sourceName.equals("Detention Sphere")) {
// existing "exile until leaves" enchantments only target

View File

@@ -64,7 +64,7 @@ public class PlayAi extends SpellAbilityAi {
return false;
}
} else if (!sa.hasParam("Valid")) {
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
cards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
if (cards.isEmpty()) {
return false;
}
@@ -159,12 +159,8 @@ public class PlayAi extends SpellAbilityAi {
return true;
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
*/
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
// as called from PlayEffect:173
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return true;
}
@@ -202,7 +198,7 @@ public class PlayAi extends SpellAbilityAi {
spell = (Spell) spell.copyWithDefinedCost(abCost);
}
if( AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !isOptional, true)) {
if (AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !(isOptional || sa.hasParam("Optional")), true)) {
// Before accepting, see if the spell has a valid number of targets (it should at this point).
// Proceeding past this point if the spell is not correctly targeted will result
// in "Failed to add to stack" error and the card disappearing from the game completely.

View File

@@ -30,13 +30,12 @@ public class PowerExchangeAi extends SpellAbilityAi {
sa.resetTargets();
List<Card> list =
CardLists.getValidCards(ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
// AI won't try to grab cards that are filtered out of AI decks on
// purpose
CardLists.getValidCards(ai.getGame().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
// AI won't try to grab cards that are filtered out of AI decks on purpose
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return !ComputerUtilCard.isCardRemAIDeck(c) && c.canBeTargetedBy(sa);
return !ComputerUtilCard.isCardRemAIDeck(c) && c.canBeTargetedBy(sa) && c.getController() != ai;
}
});
CardLists.sortByPowerAsc(list);

View File

@@ -202,11 +202,10 @@ public class ProtectAi extends SpellAbilityAi {
sa.resetTargets();
CardCollection list = getProtectCreatures(ai, sa);
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), source, sa);
if (game.getStack().isEmpty()) {
// If the cost is tapping, don't activate before declare
// attack/block
// If the cost is tapping, don't activate before declare attack/block
if (sa.getPayCosts().hasTapCost()) {
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
&& game.getPhaseHandler().isPlayerTurn(ai)) {
@@ -341,7 +340,7 @@ public class ProtectAi extends SpellAbilityAi {
sa.getTargets().add(c);
}
if (sa.getTargets().size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
if (sa.getTargets().size() < tgt.getMinTargets(source, sa)) {
sa.resetTargets();
return false;
}

View File

@@ -382,8 +382,7 @@ public class PumpAi extends PumpAiBase {
return false;
}
// when this happens we need to expand AI to consider if its ok for
// everything?
// when this happens we need to expand AI to consider if its ok for everything?
for (final Card card : cards) {
if (sa.isCurse()) {
if (!card.getController().isOpponentOf(ai)) {
@@ -400,7 +399,6 @@ public class PumpAi extends PumpAiBase {
if (ComputerUtilCard.shouldPumpCard(ai, sa, card, defense, attack, keywords, false)) {
return true;
} else if (containsUsefulKeyword(ai, keywords, card, sa, attack)) {
Card pumped = ComputerUtilCard.getPumpedCreature(ai, sa, card, 0, 0, keywords);
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS, ai)
|| game.getPhaseHandler().is(PhaseType.COMBAT_BEGIN, ai)) {
@@ -438,7 +436,7 @@ public class PumpAi extends PumpAiBase {
&& game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& !(sa.isCurse() && defense < 0)
&& !containsNonCombatKeyword(keywords)
&& !sa.hasParam("UntilYourNextTurn")
&& !"UntilYourNextTurn".equals(sa.getParam("Duration"))
&& !"Snapcaster".equals(sa.getParam("AILogic"))
&& !isFight) {
return false;
@@ -489,7 +487,31 @@ public class PumpAi extends PumpAiBase {
// each player sacrifices one permanent, e.g. Vaevictis, Asmadi the Dire - grab the worst for allied and
// the best for opponents
return SacrificeAi.doSacOneEachLogic(ai, sa);
} else if (sa.getParam("AILogic").equals("Destroy")) {
List<Card> tgts = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
if (tgts.isEmpty()) {
return false;
}
List<Card> alliedTgts = CardLists.filter(tgts, Predicates.or(CardPredicates.isControlledByAnyOf(ai.getAllies()), CardPredicates.isController(ai)));
List<Card> oppTgts = CardLists.filter(tgts, CardPredicates.isControlledByAnyOf(ai.getRegisteredOpponents()));
Card destroyTgt = null;
if (!oppTgts.isEmpty()) {
destroyTgt = ComputerUtilCard.getBestAI(oppTgts);
} else {
// TODO: somehow limit this so that the AI doesn't always destroy own stuff when able?
destroyTgt = ComputerUtilCard.getWorstAI(alliedTgts);
}
if (destroyTgt != null) {
sa.getTargets().add(destroyTgt);
return true;
}
return false;
}
if (isFight) {
return FightAi.canFightAi(ai, sa, attack, defense);
}
@@ -518,8 +540,7 @@ public class PumpAi extends PumpAiBase {
list = CardLists.getValidCards(list, tgt.getValidTgts(), ai, source, sa);
if (game.getStack().isEmpty()) {
// If the cost is tapping, don't activate before declare
// attack/block
// If the cost is tapping, don't activate before declare attack/block
if (sa.getPayCosts().hasTapCost()) {
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
&& game.getPhaseHandler().isPlayerTurn(ai)) {

View File

@@ -6,6 +6,7 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
@@ -43,7 +44,6 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return false;
}
public boolean grantsUsefulExtraBlockOpts(final Player ai, final SpellAbility sa, final Card card, List<String> keywords) {
PhaseHandler ph = ai.getGame().getPhaseHandler();
Card pumped = ComputerUtilCard.getPumpedCreature(ai, sa, card, 0, 0, keywords);
@@ -109,7 +109,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
&& (card.getNetCombatDamage() > 0)
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS);
} else if (keyword.endsWith("CARDNAME can't attack or block.")) {
if (sa.hasParam("UntilYourNextTurn")) {
if ("UntilYourNextTurn".equals(sa.getParam("Duration"))) {
return CombatUtil.canAttack(card, ai) || CombatUtil.canBlock(card, true);
}
if (!ph.isPlayerTurn(ai)) {
@@ -153,14 +153,6 @@ public abstract class PumpAiBase extends SpellAbilityAi {
}
});
return CombatUtil.canBlockAtLeastOne(card, attackers);
} else if (keyword.endsWith("CantBlockCardUIDSource")) { // can't block CARDNAME this turn
if (!ph.isPlayerTurn(ai) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|| ph.getPhase().isBefore(PhaseType.MAIN1) || !CombatUtil.canBlock(sa.getHostCard(), card)) {
return false;
}
// target needs to be a creature, controlled by the player which is attacked
return !sa.getHostCard().isTapped() || (combat != null && combat.isAttacking(sa.getHostCard())
&& card.getController().equals(combat.getDefenderPlayerByAttacker(sa.getHostCard())));
} else if (keyword.endsWith("This card doesn't untap during your next untap step.")) {
return !ph.getPhase().isBefore(PhaseType.MAIN2) && !card.isUntapped() && ph.isPlayerTurn(ai)
&& Untap.canUntap(card);
@@ -201,7 +193,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
final Game game = ai.getGame();
final Combat combat = game.getCombat();
final PhaseHandler ph = game.getPhaseHandler();
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
final int newPower = card.getNetCombatDamage() + attack;
//int defense = getNumDefense(sa);
if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) {
@@ -479,7 +471,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
}
}); // leaves all creatures that will be destroyed
} // -X/-X end
else if (attack < 0 && !game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
else if (attack < 0 && !game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
// spells that give -X/0
boolean isMyTurn = game.getPhaseHandler().isPlayerTurn(ai);
if (isMyTurn) {
@@ -513,7 +505,6 @@ public abstract class PumpAiBase extends SpellAbilityAi {
else {
final boolean addsKeywords = !keywords.isEmpty();
if (addsKeywords) {
// If the keyword can prevent a creature from attacking, see if there's some kind of viable prioritization
if (keywords.contains("CARDNAME can't attack.") || keywords.contains("CARDNAME can't attack or block.")
|| keywords.contains("HIDDEN CARDNAME can't attack.") || keywords.contains("HIDDEN CARDNAME can't attack or block.")) {
@@ -539,8 +530,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
protected boolean containsNonCombatKeyword(final List<String> keywords) {
for (final String keyword : keywords) {
// since most keywords are combat relevant check for those that are
// not
// since most keywords are combat relevant check for those that are not
if (keyword.endsWith("This card doesn't untap during your next untap step.")
|| keyword.endsWith("Shroud") || keyword.endsWith("Hexproof")) {
return true;

View File

@@ -48,12 +48,6 @@ public class PumpAllAi extends PumpAiBase {
}
}
final int power = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumAtt"), sa);
final int defense = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumDef"), sa);
final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<>();
final PhaseType phase = game.getPhaseHandler().getPhase();
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false;
}
@@ -64,31 +58,34 @@ public class PumpAllAi extends PumpAiBase {
}
}
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
final Player opp = ai.getWeakestOpponent();
CardCollection comp = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source, sa);
CardCollection human = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source, sa);
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null && sa.canTarget(opp) && sa.hasParam("IsCurse")) {
final Player opp = ai.getStrongestOpponent();
if (tgt != null && sa.canTarget(opp) && sa.isCurse()) {
sa.resetTargets();
sa.getTargets().add(opp);
return true;
}
if (tgt != null && sa.canTarget(ai) && !sa.hasParam("IsCurse")) {
if (tgt != null && sa.canTarget(ai) && !sa.isCurse()) {
sa.resetTargets();
sa.getTargets().add(ai);
return true;
}
if (!game.getStack().isEmpty() && !sa.isCurse()) {
return pumpAgainstRemoval(ai, sa, comp);
final int power = AbilityUtils.calculateAmount(source, sa.getParam("NumAtt"), sa);
final int defense = AbilityUtils.calculateAmount(source, sa.getParam("NumDef"), sa);
final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<>();
final PhaseType phase = game.getPhaseHandler().getPhase();
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
if (sa.hasParam("IsCurse")) {
CardCollection comp = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source, sa);
CardCollection human = CardLists.getValidCards(opp.getCardsIn(ZoneType.Battlefield), valid, source.getController(), source, sa);
if (sa.isCurse()) {
if (defense < 0) { // try to destroy creatures
comp = CardLists.filter(comp, new Predicate<Card>() {
@Override
@@ -113,7 +110,7 @@ public class PumpAllAi extends PumpAiBase {
if (phase.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|| phase.isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())
|| game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|| game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
return false;
}
int totalPower = 0;
@@ -139,11 +136,14 @@ public class PumpAllAi extends PumpAiBase {
return true;
}
// evaluate both lists and pass only if human creatures are more
// valuable
// evaluate both lists and pass only if human creatures are more valuable
return (ComputerUtilCard.evaluateCreatureList(comp) + 200) < ComputerUtilCard.evaluateCreatureList(human);
} // end Curse
if (!game.getStack().isEmpty()) {
return pumpAgainstRemoval(ai, sa, comp);
}
return !CardLists.getValidCards(getPumpCreatures(ai, sa, defense, power, keywords, false), valid, source.getController(), source, sa).isEmpty();
} // pumpAllCanPlayAI()
@@ -152,6 +152,17 @@ public class PumpAllAi extends PumpAiBase {
return true;
}
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
// it might help so take it
if (!sa.usesTargeting() && !sa.isCurse() && sa.getParam("ValidCards") != null && sa.getParam("ValidCards").contains("YouCtrl")) {
return true;
}
// important to call canPlay first so targets are added if needed
return canPlayAI(ai, sa) || mandatory;
}
boolean pumpAgainstRemoval(Player ai, SpellAbility sa, List<Card> comp) {
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa, true);
for (final Card c : comp) {

View File

@@ -49,14 +49,6 @@ import forge.game.zone.ZoneType;
*/
public class RegenerateAi extends SpellAbilityAi {
// Ex: A:SP$Regenerate | Cost$W | ValidTgts$ Creature | TgtPrompt$ Select target creature | SpellDescription$Regenerate
// target creature.
// http://www.slightlymagic.net/wiki/Forge_AbilityFactory#Regenerate
// **************************************************************
// ********************* Regenerate ****************************
// **************************************************************
@Override
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
final Game game = ai.getGame();
@@ -65,8 +57,7 @@ public class RegenerateAi extends SpellAbilityAi {
boolean chance = false;
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt == null) {
// As far as I can tell these Defined Cards will only have one of
// them
// As far as I can tell these Defined Cards will only have one of them
final List<Card> list = AbilityUtils.getDefinedCards(hostCard, sa.getParam("Defined"), sa);
if (!game.getStack().isEmpty()) {
@@ -105,8 +96,7 @@ public class RegenerateAi extends SpellAbilityAi {
}
if (!game.getStack().isEmpty()) {
// check stack for something on the stack will kill anything i
// control
// check stack for something on the stack will kill anything i control
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa, true);
final List<Card> threatenedTargets = new ArrayList<>();
@@ -191,8 +181,7 @@ public class RegenerateAi extends SpellAbilityAi {
}
}
// TODO see if something on the stack is about to kill something i
// can target
// TODO see if something on the stack is about to kill something i can target
// choose my best X without regen
if (CardLists.getNotType(compTargetables, "Creature").isEmpty()) {

View File

@@ -1,6 +1,7 @@
package forge.ai.ability;
import forge.ai.AiAttackController;
import forge.ai.AiController;
import forge.ai.ComputerUtilCost;
import forge.ai.PlayerControllerAi;
@@ -14,10 +15,10 @@ public class RepeatAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (sa.usesTargeting()) {
if (!opp.canBeTargetedBy(sa)) {
if (!sa.canTarget(opp)) {
return false;
}
sa.resetTargets();
@@ -44,9 +45,8 @@ public class RepeatAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) {
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (sa.canTarget(opp)) {
sa.resetTargets();
sa.getTargets().add(opp);

View File

@@ -16,7 +16,6 @@ import forge.game.card.CardPredicates.Presets;
import forge.game.card.CardUtil;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.TextUtil;
@@ -67,7 +66,7 @@ public class RepeatEachAi extends SpellAbilityAi {
return false;
} else if ("AllPlayerLoseLife".equals(logic)) {
final Card source = sa.getHostCard();
AbilitySub repeat = sa.getAdditionalAbility("RepeatSubAbility");
SpellAbility repeat = sa.getAdditionalAbility("RepeatSubAbility");
String svar = repeat.getSVar(repeat.getParam("LifeAmount"));
// replace RememberedPlayerCtrl with YouCtrl

View File

@@ -20,13 +20,9 @@ import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
public class SacrificeAi extends SpellAbilityAi {
// **************************************************************
// *************************** Sacrifice ***********************
// **************************************************************
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
return sacrificeTgtAI(ai, sa);
}
@@ -48,8 +44,7 @@ public class SacrificeAi extends SpellAbilityAi {
}
// Improve AI for triggers. If source is a creature with:
// When ETB, sacrifice a creature. Check to see if the AI has something
// to sacrifice
// When ETB, sacrifice a creature. Check to see if the AI has something to sacrifice
// Eventually, we can call the trigger of ETB abilities with not
// mandatory as part of the checks to cast something
@@ -58,12 +53,11 @@ public class SacrificeAi extends SpellAbilityAi {
}
private boolean sacrificeTgtAI(final Player ai, final SpellAbility sa) {
final Card source = sa.getHostCard();
final TargetRestrictions tgt = sa.getTargetRestrictions();
final boolean destroy = sa.hasParam("Destroy");
Player opp = ai.getWeakestOpponent();
Player opp = ai.getStrongestOpponent();
if (tgt != null) {
sa.resetTargets();
@@ -109,8 +103,7 @@ public class SacrificeAi extends SpellAbilityAi {
sa.setXManaCostPaid(Math.min(ComputerUtilCost.getMaxXValue(sa, ai), amount));
}
final int half = (amount / 2) + (amount % 2); // Half of amount
// rounded up
final int half = (amount / 2) + (amount % 2); // Half of amount rounded up
// If the Human has at least half rounded up of the amount to be
// sacrificed, cast the spell
@@ -130,8 +123,7 @@ public class SacrificeAi extends SpellAbilityAi {
// If Sacrifice hits both players:
// Only cast it if Human has the full amount of valid
// Only cast it if AI doesn't have the full amount of Valid
// TODO: Cast if the type is favorable: my "worst" valid is
// worse than his "worst" valid
// TODO: Cast if the type is favorable: my "worst" valid is worse than his "worst" valid
final String num = sa.hasParam("Amount") ? sa.getParam("Amount") : "1";
int amount = AbilityUtils.calculateAmount(source, num, sa);

View File

@@ -2,17 +2,12 @@ package forge.ai.ability;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost;
import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import forge.util.TextUtil;
public class SacrificeAllAi extends SpellAbilityAi {
@@ -22,22 +17,7 @@ public class SacrificeAllAi extends SpellAbilityAi {
// based on what the expected targets could be
final Cost abCost = sa.getPayCosts();
final Card source = sa.getHostCard();
String valid = "";
if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards");
}
if (valid.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
valid = TextUtil.fastReplace(valid, "X", Integer.toString(xPay));
}
CardCollection humanlist =
CardLists.getValidCards(ai.getWeakestOpponent().getCardsIn(ZoneType.Battlefield), valid.split(","), source.getController(), source, sa);
CardCollection computerlist =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), source.getController(), source, sa);
final String logic = sa.getParamOrDefault("AILogic", "");
if (abCost != null) {
// AI currently disabled for some costs
@@ -46,29 +26,19 @@ public class SacrificeAllAi extends SpellAbilityAi {
}
}
// prevent run-away activations - first time will always return true
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
if (logic.equals("HellionEruption")) {
if (ai.getCreaturesInPlay().size() < 5 || ai.getCreaturesInPlay().size() * 150 < ComputerUtilCard.evaluateCreatureList(ai.getCreaturesInPlay())) {
return false;
}
}
// if only creatures are affected evaluate both lists and pass only if
// human creatures are more valuable
if ((CardLists.getNotType(humanlist, "Creature").size() == 0) && (CardLists.getNotType(computerlist, "Creature").size() == 0)) {
if ((ComputerUtilCard.evaluateCreatureList(computerlist) + 200) >= ComputerUtilCard
.evaluateCreatureList(humanlist)) {
return false;
}
} // only lands involved
else if ((CardLists.getNotType(humanlist, "Land").size() == 0) && (CardLists.getNotType(computerlist, "Land").size() == 0)) {
if ((ComputerUtilCard.evaluatePermanentList(computerlist) + 1) >= ComputerUtilCard
.evaluatePermanentList(humanlist)) {
return false;
}
} // otherwise evaluate both lists by CMC and pass only if human
// permanents are more valuable
else if ((ComputerUtilCard.evaluatePermanentList(computerlist) + 3) >= ComputerUtilCard
.evaluatePermanentList(humanlist)) {
if (!DestroyAllAi.doMassRemovalLogic(ai, sa)) {
return false;
}
// prevent run-away activations - first time will always return true
boolean chance = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
return ((MyRandom.getRandom().nextFloat() < .9667) && chance);
}

View File

@@ -3,14 +3,12 @@ package forge.ai.ability;
import com.google.common.base.Predicates;
import forge.ai.ComputerUtilMana;
import forge.ai.SpecialCardAi;
import forge.ai.SpellAbilityAi;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.Card.SplitCMCMode;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
@@ -47,6 +45,11 @@ public class ScryAi extends SpellAbilityAi {
*/
@Override
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
// For Brain in a Jar, avoid competing against the other ability in the opponent's EOT.
if ("BrainJar".equals(sa.getParam("AILogic"))) {
return ph.getPhase().isAfter(PhaseType.MAIN2);
}
// if the Scry ability requires tapping and has a mana cost, it's best done at the end of opponent's turn
// 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,
@@ -102,56 +105,9 @@ public class ScryAi extends SpellAbilityAi {
if ("Never".equals(aiLogic)) {
return false;
} else if ("BrainJar".equals(aiLogic)) {
final Card source = sa.getHostCard();
int counterNum = source.getCounters(CounterEnumType.CHARGE);
// no need for logic
if (counterNum == 0) {
return false;
}
int libsize = ai.getCardsIn(ZoneType.Library).size();
final CardCollection hand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), Predicates.or(
CardPredicates.isType("Instant"), CardPredicates.isType("Sorcery")));
if (!hand.isEmpty()) {
// has spell that can be cast in hand with put ability
if (!CardLists.filter(hand, CardPredicates.hasCMC(counterNum + 1)).isEmpty()) {
return false;
}
// has spell that can be cast if one counter is removed
if (!CardLists.filter(hand, CardPredicates.hasCMC(counterNum)).isEmpty()) {
sa.setXManaCostPaid(1);
return true;
}
}
final CardCollection library = CardLists.filter(ai.getCardsIn(ZoneType.Library), Predicates.or(
CardPredicates.isType("Instant"), CardPredicates.isType("Sorcery")));
if (!library.isEmpty()) {
// get max cmc of instant or sorceries in the libary
int maxCMC = 0;
for (final Card c : library) {
int v = c.getCMC();
if (c.isSplitCard()) {
v = Math.max(c.getCMC(SplitCMCMode.LeftSplitCMC), c.getCMC(SplitCMCMode.RightSplitCMC));
}
if (v > maxCMC) {
maxCMC = v;
}
}
// there is a spell with more CMC, no need to remove counter
if (counterNum + 1 < maxCMC) {
return false;
}
int maxToRemove = counterNum - maxCMC + 1;
// no Scry 0, even if its catched from later stuff
if (maxToRemove <= 0) {
return false;
}
sa.setXManaCostPaid(maxToRemove);
} else {
// no Instant or Sorceries anymore, just scry
sa.setXManaCostPaid(Math.min(counterNum, libsize));
}
return SpecialCardAi.BrainInAJar.consider(ai, sa);
} else if ("MultipleChoice".equals(aiLogic)) {
return SpecialCardAi.MultipleChoice.consider(ai, sa);
}
return true;
}
@@ -163,11 +119,9 @@ public class ScryAi extends SpellAbilityAi {
return false;
}
double chance = .4; // 40 percent chance of milling with instant speed
// stuff
double chance = .4; // 40 percent chance of milling with instant speed stuff
if (SpellAbilityAi.isSorcerySpeed(sa)) {
chance = .667; // 66.7% chance for sorcery speed (since it will
// never activate EOT)
chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT)
}
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1);

View File

@@ -52,8 +52,7 @@ public class SetStateAi extends SpellAbilityAi {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
// Gross generalization, but this always considers alternate
// states more powerful
// Gross generalization, but this always considers alternate states more powerful
return !sa.getHostCard().isInAlternateState();
}

View File

@@ -17,8 +17,7 @@ public class ShuffleAi extends SpellAbilityAi {
return aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN2, aiPlayer);
}
// not really sure when the compy would use this; maybe only after a
// human
// not really sure when the compy would use this; maybe only after a human
// deliberately put a card on top of their library
return false;
/*

View File

@@ -1,5 +1,6 @@
package forge.ai.ability;
import forge.ai.AiAttackController;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
@@ -8,16 +9,33 @@ import forge.game.spellability.SpellAbility;
public class SkipPhaseAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
return true;
return targetPlayer(aiPlayer, sa, false);
}
@Override
protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) {
return mandatory || canPlayAI(aiPlayer, sa);
return targetPlayer(aiPlayer, sa, mandatory);
}
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return true;
}
public boolean targetPlayer(Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) {
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
sa.resetTargets();
if (sa.canTarget(opp)) {
sa.getTargets().add(opp);
}
else if (mandatory && sa.canTarget(ai)) {
sa.getTargets().add(ai);
}
else {
return false;
}
}
return true;
}
}

View File

@@ -1,6 +1,5 @@
package forge.ai.ability;
import forge.ai.SpellAbilityAi;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;

View File

@@ -66,7 +66,6 @@ public class TapAi extends TapAiBase {
// Set PayX here to maximum value.
// TODO need to set XManaCostPaid for targets, maybe doesn't need PayX anymore?
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai));
// TODO since change of PayX. the shouldCastLessThanMax logic might be faulty
}
sa.resetTargets();

View File

@@ -5,11 +5,13 @@ import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollection;
@@ -109,7 +111,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
* @return a boolean.
*/
protected boolean tapPrefTargeting(final Player ai, final Card source, final SpellAbility sa, final boolean mandatory) {
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
final Game game = ai.getGame();
CardCollection tapList = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
tapList = CardLists.getTargetableCards(tapList, sa);
@@ -308,9 +310,8 @@ public abstract class TapAiBase extends SpellAbilityAi {
return true;
}
// TODO: use Defined to determine, if this is an unfavorable result
return true;
final List<Card> pDefined = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
return pDefined.isEmpty() || (pDefined.get(0).isUntapped() && pDefined.get(0).getController() != ai);
} else {
sa.resetTargets();
if (tapPrefTargeting(ai, source, sa, mandatory)) {

View File

@@ -12,7 +12,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CardPredicates;
import forge.game.combat.CombatUtil;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
@@ -49,7 +49,7 @@ public class TapAllAi extends SpellAbilityAi {
}
validTappables = CardLists.getValidCards(validTappables, valid, source.getController(), source, sa);
validTappables = CardLists.filter(validTappables, Presets.UNTAPPED);
validTappables = CardLists.filter(validTappables, CardPredicates.Presets.UNTAPPED);
if (sa.hasParam("AILogic")) {
String logic = sa.getParam("AILogic");
@@ -69,18 +69,8 @@ public class TapAllAi extends SpellAbilityAi {
return false;
}
final List<Card> human = CardLists.filter(validTappables, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getController().equals(opp);
}
});
final List<Card> compy = CardLists.filter(validTappables, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getController().equals(ai);
}
});
final List<Card> human = CardLists.filterControlledBy(validTappables, opp);
final List<Card> compy = CardLists.filterControlledBy(validTappables, ai);
if (human.size() <= compy.size()) {
return false;
}
@@ -102,7 +92,7 @@ public class TapAllAi extends SpellAbilityAi {
final Game game = source.getGame();
CardCollectionView tmpList = game.getCardsIn(ZoneType.Battlefield);
tmpList = CardLists.getValidCards(tmpList, valid, source.getController(), source, sa);
tmpList = CardLists.filter(tmpList, Presets.UNTAPPED);
tmpList = CardLists.filter(tmpList, CardPredicates.Presets.UNTAPPED);
return tmpList;
}

View File

@@ -19,8 +19,7 @@ public class TapOrUntapAi extends TapAiBase {
if (!sa.usesTargeting()) {
// assume we are looking to tap human's stuff
// TODO - check for things with untap abilities, and don't tap
// those.
// TODO - check for things with untap abilities, and don't tap those.
boolean bFlag = false;
for (final Card c : AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa)) {
@@ -40,6 +39,4 @@ public class TapOrUntapAi extends TapAiBase {
return randomReturn;
}
}

View File

@@ -50,7 +50,6 @@ public class TokenAi extends SpellAbilityAi {
@Override
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
final Card source = sa.getHostCard();
// Planeswalker-related flags
boolean pwMinus = false;
@@ -96,7 +95,7 @@ public class TokenAi extends SpellAbilityAi {
if (sa.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
x = ComputerUtilCost.getMaxXValue(sa, ai);
sa.setXManaCostPaid(x);
sa.getRootAbility().setXManaCostPaid(x);
}
if (x <= 0) {
return false; // 0 tokens or 0 toughness token(s)
@@ -143,7 +142,7 @@ public class TokenAi extends SpellAbilityAi {
final Player opp = ai.getWeakestOpponent();
if (ComputerUtil.preventRunAwayActivations(sa)) {
return false; // prevent infinite tokens?
return false; // prevent infinite tokens?
}
Card actualToken = spawnToken(ai, sa);
@@ -359,7 +358,7 @@ public class TokenAi extends SpellAbilityAi {
if (!sa.hasParam("TokenScript")) {
throw new RuntimeException("Spell Ability has no TokenScript: " + sa);
}
Card result = TokenInfo.getProtoType(sa.getParam("TokenScript"), sa);
Card result = TokenInfo.getProtoType(sa.getParam("TokenScript"), sa, ai);
if (result == null) {
throw new RuntimeException("don't find Token for TokenScript: " + sa.getParam("TokenScript"));

View File

@@ -2,6 +2,7 @@ package forge.ai.ability;
import java.util.List;
import forge.ai.AiAttackController;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
@@ -28,7 +29,7 @@ public class TwoPilesAi extends SpellAbilityAi {
valid = sa.getParam("ValidCards");
}
final Player opp = ai.getWeakestOpponent();
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {

View File

@@ -56,7 +56,6 @@ public class UnattachAllAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final Card card = sa.getHostCard();
final Player opp = ai.getWeakestOpponent();
// Check if there are any valid targets
List<GameObject> targets = new ArrayList<>();
final TargetRestrictions tgt = sa.getTargetRestrictions();
@@ -66,8 +65,8 @@ public class UnattachAllAi extends SpellAbilityAi {
if (!mandatory && card.isEquipment() && !targets.isEmpty()) {
Card newTarget = (Card) targets.get(0);
//don't equip human creatures
if (newTarget.getController().equals(opp)) {
//don't equip opponent creatures
if (!newTarget.getController().equals(ai)) {
return false;
}

View File

@@ -6,6 +6,7 @@ import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import forge.ai.AiAttackController;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard;
@@ -65,7 +66,7 @@ public class UntapAi extends SpellAbilityAi {
if (!sa.usesTargeting()) {
final List<Card> pDefined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
return pDefined == null || !pDefined.get(0).isUntapped() || pDefined.get(0).getController() != ai;
return pDefined.isEmpty() || (pDefined.get(0).isTapped() && pDefined.get(0).getController() == ai);
} else {
return untapPrefTargeting(ai, sa, false);
}
@@ -81,9 +82,8 @@ public class UntapAi extends SpellAbilityAi {
return false;
}
// TODO: use Defined to determine, if this is an unfavorable result
final List<Card> pDefined = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
return pDefined == null || !pDefined.get(0).isUntapped() || pDefined.get(0).getController() != ai;
return pDefined.isEmpty() || (pDefined.get(0).isTapped() && pDefined.get(0).getController() == ai);
} else {
if (untapPrefTargeting(ai, sa, mandatory)) {
return true;
@@ -130,7 +130,8 @@ public class UntapAi extends SpellAbilityAi {
Player targetController = ai;
if (sa.isCurse()) {
targetController = ai.getWeakestOpponent();
// TODO search through all opponents, may need to check if different controllers allowed
targetController = AiAttackController.choosePreferredDefenderPlayer(ai);
}
CardCollection list = CardLists.getTargetableCards(targetController.getCardsIn(ZoneType.Battlefield), sa);
@@ -149,8 +150,7 @@ public class UntapAi extends SpellAbilityAi {
}
CardCollection untapList = targetUntapped ? list : CardLists.filter(list, Presets.TAPPED);
// filter out enchantments and planeswalkers, their tapped state doesn't
// matter.
// filter out enchantments and planeswalkers, their tapped state doesn't matter.
final String[] tappablePermanents = {"Creature", "Land", "Artifact"};
untapList = CardLists.getValidCards(untapList, tappablePermanents, source.getController(), source, sa);
@@ -374,7 +374,7 @@ public class UntapAi extends SpellAbilityAi {
if (!ComputerUtilMana.hasEnoughManaSourcesToCast(ab, ai)) {
// TODO: Currently limited to predicting something that can be paid with any color,
// can ideally be improved to work by color.
ManaCostBeingPaid reduced = new ManaCostBeingPaid(ab.getPayCosts().getCostMana().getManaCostFor(ab), ab.getPayCosts().getCostMana().getRestiction());
ManaCostBeingPaid reduced = new ManaCostBeingPaid(ab.getPayCosts().getCostMana().getManaCostFor(ab), ab.getPayCosts().getCostMana().getRestriction());
reduced.decreaseShard(ManaCostShard.GENERIC, untappingCards.size());
if (ComputerUtilMana.canPayManaCost(reduced, ab, ai)) {
CardCollection manaLandsTapped = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield),

View File

@@ -8,6 +8,7 @@ import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerCollection;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
@@ -29,6 +30,10 @@ public class UntapAllAi extends SpellAbilityAi {
valid = sa.getParam("ValidCards");
}
list = CardLists.getValidCards(list, valid.split(","), source.getController(), source, sa);
// don't untap if only opponent benefits
PlayerCollection goodControllers = aiPlayer.getAllies();
goodControllers.add(aiPlayer);
list = CardLists.filter(list, CardPredicates.isControlledByAnyOf(goodControllers));
return !list.isEmpty();
}
return false;

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