Compare commits

...

887 Commits

Author SHA1 Message Date
Blacksmith
5b39be5fe9 [maven-release-plugin] prepare release forge-1.6.23 2019-04-07 19:48:50 +00:00
Blacksmith
f6b38bf9ee Update README.txt for release 2019-04-07 19:46:31 +00:00
swordshine
af7cb0ae30 Merge branch 'fix-card-auditer' into 'master'
Add variants for Card Auditer

See merge request core-developers/forge!1516
2019-04-07 03:51:01 +00:00
Chris H
38b183b795 Add variants for Card Auditer 2019-04-06 23:26:20 -04:00
Sol
06afb22fd7 Merge branch 'patch-2' into 'master'
Update ANNOUNCEMENTS.txt

See merge request core-developers/forge!1515
2019-04-07 03:10:04 +00:00
Sol
04cd2e8c45 Update ANNOUNCEMENTS.txt 2019-04-07 03:09:52 +00:00
Sol
6cf73e2a5d Merge branch 'patch' into 'master'
Update Typelists

See merge request core-developers/forge!1514
2019-04-07 02:13:37 +00:00
Sol
28ee9eb58d Merge branch 'Nigol-master-patch-38850' into 'master'
g_2_2_monkey PT should be 2/2 not 1/1

See merge request core-developers/forge!1513
2019-04-07 02:13:14 +00:00
Marc-André
6a755db00d g_2_2_monkey PT should be 2/2 not 1/1 2019-04-06 07:35:01 +00:00
swordshine
2bd008328a Update Typelists 2019-04-06 15:33:39 +08:00
Michael Kamensky
9feb583413 Merge branch 'sealed-mode-singleton-round-limits' into 'master'
Build up to 7 extra sealed pools, based on numbers

See merge request core-developers/forge!1509
2019-04-06 03:37:24 +00:00
Sol
6ee9f14e59 Merge branch 'Nigol-master-patch-52687' into 'master'
Update forge-gui/res/tokenscripts/r_0_1_elemental_rekindling_phoenix.txt

Closes #935

See merge request core-developers/forge!1511
2019-04-06 02:51:00 +00:00
Marc-André
2cf8ea8ae9 Update forge-gui/res/tokenscripts/r_0_1_elemental_rekindling_phoenix.txt 2019-04-06 01:14:17 +00:00
swordshine
687972782a Merge branch 'warcards' into 'master'
First batch of forgescribed WAR spoilers

See merge request core-developers/forge!1503
2019-04-05 03:46:18 +00:00
swordshine
c67ea9cb2b Merge branch 'patch-1' into 'master'
Patch 1

Closes #931

See merge request core-developers/forge!1510
2019-04-05 03:45:15 +00:00
Sol
1fec85b164 Update Duels of the Planeswalkers.txt 2019-04-05 03:05:33 +00:00
Sol
1bc3f9fc0e Update Ravnica_Allegiance_Guild_Kit.txt 2019-04-05 03:02:37 +00:00
Sol
743711634e Merge branch 'Nigol-master-patch-08546' into 'master'
Fixed Card not updating in the stack on mouseover when targeting arcs isn't on Mouseover

Closes #932

See merge request core-developers/forge!1508
2019-04-05 02:59:27 +00:00
Sol
5e28d2c975 Merge branch 'patch-1' into 'master'
Update Duel Decks Anthology Garruk vs. Liliana.txt

Closes #933

See merge request core-developers/forge!1507
2019-04-05 02:58:46 +00:00
Marc-André
9c578fb57c Fixed Card not updating in the stack on mouseover when targeting arcs isn't on Mouseover 2019-04-05 02:48:44 +00:00
Chris H
e7fbe25af2 Build up to 7 extra sealed pools, based on numbers 2019-04-04 22:48:13 -04:00
Sol
a52b39f8d3 Update Duel Decks Anthology Garruk vs. Liliana.txt 2019-04-05 00:49:43 +00:00
austinio7116
07093df56d Fixed issues raised in MR discussions 2019-04-04 22:59:46 +01:00
swordshine
49931297ac Merge branch 'finalrnadeckgen' into 'master'
Final RNA deckgen data

See merge request core-developers/forge!1502
2019-04-04 07:45:42 +00:00
austinio7116
15f3034ac0 First batch of forgescribed WAR spoilers 2019-04-04 08:03:40 +01:00
austinio7116
d36651a779 Final RNA deckgen data
(cherry picked from commit f526f96)
2019-04-04 07:21:18 +01:00
Michael Kamensky
245a95b336 Merge branch 'master' into 'master'
PS_RNAR: Added some cards to the human library.

See merge request core-developers/forge!1500
2019-04-04 05:01:51 +00:00
Agetian
22e2a42807 - PS_RNAR: Added some cards to the human library. 2019-04-04 08:01:13 +03:00
Michael Kamensky
1c1d91c149 Merge branch 'master' into 'master'
Added puzzle PS_RNAR (Possibility Storm - Ravnica Allegiance Preview Card Puzzle Revisited)

See merge request core-developers/forge!1499
2019-04-04 04:52:31 +00:00
Agetian
a8a2d7bbeb - Added puzzle PS_RNAR (Possibility Storm - Ravnica Allegiance Preview Card Puzzle Revisited) 2019-04-04 07:51:57 +03:00
Michael Kamensky
02064e05df Merge branch 'patch' into 'master'
Fixed All Hallow's Eve

See merge request core-developers/forge!1497
2019-04-04 03:57:49 +00:00
Michael Kamensky
c00add6050 Merge branch 'clear-image-cache' into 'master'
Token Viewer 1.0 + Clear Image Cache button

Closes #876

See merge request core-developers/forge!1496
2019-04-04 03:57:39 +00:00
Sol
04dfdac71c Merge branch 'patch-1' into 'master'
Update infernal_reckoning.txt

See merge request core-developers/forge!1498
2019-04-04 02:35:01 +00:00
Sol
8d4566af3e Update infernal_reckoning.txt 2019-04-04 01:56:37 +00:00
swordshine
3cc3ef0b13 Fixed All Hallow's Eve 2019-04-02 16:03:59 +08:00
Chris H
f5a4360339 TokenViewer doesn't crash now.. Just need it to display 2019-03-31 22:48:57 -04:00
Michael Kamensky
cb8ad8221e Merge branch 'master' into 'master'
Support for three color lands' sounds

See merge request core-developers/forge!1494
2019-03-31 05:25:19 +00:00
Chris H
a050f7c26c TokenViewer Deck "editor" 2019-03-30 22:09:16 -04:00
Alessandro Coli
0a709a6cf4 Support for three color lands 2019-03-30 15:59:44 +01:00
Alessandro Coli
a9124743ef Support for three color lands 2019-03-30 15:58:49 +01:00
Sol
42f6cdbb46 Merge branch 'patch-1' into 'master'
Fix elf knight token name

See merge request core-developers/forge!1493
2019-03-30 01:14:35 +00:00
Sol
17e7883996 Fix elf knight token name 2019-03-30 01:13:14 +00:00
Michael Kamensky
91a8d1761a Merge branch 'fix-spellskite' into 'master'
Spellskite should grab its magnet target from DefinedMagnet (fixes Spellskite not working)

Closes #925

See merge request core-developers/forge!1492
2019-03-28 16:41:32 +00:00
Agetian
9c39d159e9 - Spellskite should grab its magnet target from DefinedMagnet. 2019-03-28 17:34:53 +03:00
Michael Kamensky
59abbfd51f Merge branch 'patch-1' into 'master'
Update assemble_the_rank_and_vile.txt

Closes #917

See merge request core-developers/forge!1491
2019-03-28 11:28:33 +00:00
Hans Mackowiak
953de7d6f1 Update assemble_the_rank_and_vile.txt 2019-03-28 11:28:33 +00:00
Rob Schnautz
51e9e2b692 Merge branch 'patch-1' into 'master'
Update token-images.txt

See merge request core-developers/forge!1490
2019-03-27 13:24:54 +00:00
Hans Mackowiak
9447561d53 Update token-images.txt 2019-03-27 13:24:54 +00:00
Michael Kamensky
928f903028 Merge branch 'master' into 'master'
Added puzzle PS_RNA9 (Possibility Storm - Ravnica Allegiance 09).

See merge request core-developers/forge!1489
2019-03-27 06:12:42 +00:00
Agetian
bc4826ed77 - Added puzzle PS_RNA9. 2019-03-27 09:11:49 +03:00
Michael Kamensky
88a674240d Merge branch 'tokenscripts' into 'master'
resolve #926

Closes #926

See merge request core-developers/forge!1488
2019-03-27 04:02:20 +00:00
Michael Kamensky
1d4b9d68ed Merge branch 'deck-descriptions' into 'master'
Deck descriptions

See merge request core-developers/forge!1487
2019-03-27 04:02:08 +00:00
Adam Pantel
c1dfbc04e4 Fix missing type in tokenscripts 2019-03-26 19:22:27 -04:00
Rob Schnautz
fa80df585f Merge branch 'master' into 'deck-descriptions' 2019-03-26 15:33:03 +00:00
Rob Schnautz
2cfbf8df30 back matter 2019-03-26 15:25:45 +00:00
Rob Schnautz
ad6e9acbe1 back matter 2019-03-25 21:44:42 +00:00
Rob Schnautz
3f450e813d back matter 2019-03-25 19:51:29 +00:00
Rob Schnautz
9c62a0e2ab back matter 2019-03-25 19:37:08 +00:00
Rob Schnautz
5dc883e932 back matter 2019-03-25 18:52:34 +00:00
Rob Schnautz
94ab23f09f back matter 2019-03-25 18:46:25 +00:00
Rob Schnautz
286e5721f7 back matter 2019-03-25 18:35:24 +00:00
Rob Schnautz
eb5899ac15 back matter 2019-03-25 18:24:47 +00:00
Rob Schnautz
a1fca42b57 back matter 2019-03-25 17:53:53 +00:00
Rob Schnautz
16314d5ebc Merge branch 'MED-patch' into 'master'
Mythic Edition patch

See merge request core-developers/forge!1485
2019-03-25 16:53:08 +00:00
Rob Schnautz
d5fe891440 Fix MCI code and card numbers (MPS points to Kaladesh Inventions) 2019-03-25 16:50:28 +00:00
Rob Schnautz
13d99e209b Fix MCI code (MPS points to Kaladesh Inventions) 2019-03-25 16:48:26 +00:00
Rob Schnautz
9946155c71 Merge branch 'cherry-pick-ff7def5f' into 'master'
Amonkhet Invocations patch

See merge request core-developers/forge!1484
2019-03-25 16:43:49 +00:00
Rob Schnautz
6a6ca3f403 Fix MCI code (MPS points to Kaladesh Inventions)
(cherry picked from commit ff7def5f5afd7a07991caf2549f5fb92f9a85186)
2019-03-25 16:42:43 +00:00
Michael Kamensky
1c0d95b930 Merge branch 'draft-bugs' into 'master'
Resolve #921, #922

Closes #921 and #922

See merge request core-developers/forge!1478
2019-03-25 03:53:44 +00:00
Michael Kamensky
271b940723 Merge branch 'quest-pets-redux' into 'master'
Always create an empty map for tokenNormalized

See merge request core-developers/forge!1483
2019-03-25 03:52:52 +00:00
Michael Kamensky
8eecccff0b Merge branch 'aura-corner-case' into 'master'
resolve #923

Closes #923

See merge request core-developers/forge!1479
2019-03-25 03:52:43 +00:00
Adam Pantel
18f32a1657 Fix draft duplicates bug 2019-03-24 21:32:48 -04:00
Adam Pantel
5cd5020595 Fix draft bots running conspiracies 2019-03-24 21:32:14 -04:00
Chris H
5aee626661 Always create an empty map for tokenNormalized 2019-03-24 21:00:31 -04:00
Rob Schnautz
f2dae53c11 back matter, premium cards 2019-03-24 19:53:36 +00:00
Rob Schnautz
da93a734fb back matter, premium cards 2019-03-24 19:31:12 +00:00
Rob Schnautz
25042c9814 Back matter, premium cards 2019-03-24 18:20:02 +00:00
Rob Schnautz
a239dbfc01 More deck descriptions. 2019-03-24 17:24:53 +00:00
Rob Schnautz
1b1502af0d Merge branch 'master' into 'deck-descriptions'
Update from master

See merge request core-developers/forge!1482
2019-03-24 17:04:55 +00:00
Rob Schnautz
6ca034eab9 React to removal of Spike Weaver by using the effect if it's already planning to do later
Don't leave blockers if planning to use fog effect, except the card that will do so. (might need to tap to use it, or might get killed if attacking)
2019-03-24 17:04:53 +00:00
Hans Mackowiak
99456c7bbb Merge branch 'patch-2' into 'master'
Fix download URL for Afterlife spirit token images.

See merge request core-developers/forge!1481
2019-03-24 16:57:08 +00:00
Rob Schnautz
c0d87aecaa author @Hanmac Fix download URL for Afterlife spirit token images. 2019-03-24 16:53:42 +00:00
Michael Kamensky
1d691a5342 Merge branch 'patch-1' into 'master'
Quest pet tokens don't have editions.

See merge request core-developers/forge!1480
2019-03-24 04:07:21 +00:00
Sol
de719b9aa5 There's no such thing as "file1_set" its either "file_set" or "file2_set" 2019-03-24 01:43:16 +00:00
Sol
190a56181e Quest pet tokens don't have editions. 2019-03-24 00:55:24 +00:00
Adam Pantel
ae772e99a7 Enchant fix 2019-03-23 14:35:00 -04:00
Michael Kamensky
e13ddd9666 Merge branch 'master' into 'master'
Added puzzle PS_RNA8 (Possibility Storm - Ravnica Allegiance 08)

See merge request core-developers/forge!1476
2019-03-23 09:52:39 +00:00
Agetian
7890420fb0 - Added puzzle PS_RNA8. 2019-03-23 12:51:38 +03:00
Sol
8d056be356 Merge branch 'patch-1' into 'master'
Set default art index to 1 for tokens

See merge request core-developers/forge!1475
2019-03-22 15:00:21 +00:00
Sol
b594cf2324 Set default art index to 1 for tokens 2019-03-22 15:00:21 +00:00
Michael Kamensky
f048b04c2b Merge branch 'patch-1' into 'master'
Update beckon_apparition.txt

See merge request core-developers/forge!1474
2019-03-22 04:27:50 +00:00
Michael Kamensky
fd1e8c1e7e Merge branch 'token-multiple-art' into 'master'
Allow multiple art for tokens based on edition files

See merge request core-developers/forge!1473
2019-03-22 04:27:45 +00:00
Sol
af0d4b0c48 Update beckon_apparition.txt 2019-03-22 03:08:29 +00:00
Chris H
dc917643b5 Allow multiple art for tokens based on edition files 2019-03-21 20:58:46 -04:00
Sol
6f90d27f38 Merge branch '916-more-tokenscript-problems' into 'master'
Resolve "More tokenscript problems"

Closes #916

See merge request core-developers/forge!1472
2019-03-21 02:50:14 +00:00
Hanmac
80851d515a TokenScript: more fixes 2019-03-20 21:17:32 +01:00
Michael Kamensky
6dfca97bbb Merge branch '915-missing-tokenscript-r_1_1_elemental' into 'master'
Resolve "Missing tokenscript: r_1_1_elemental"

Closes #915

See merge request core-developers/forge!1471
2019-03-20 13:54:06 +00:00
Hans Mackowiak
2a10750fdb Add new file 2019-03-20 13:38:43 +00:00
Michael Kamensky
a1976653f8 Merge branch 'master' into 'master'
Replace RemRandomDeck with the new format.

See merge request core-developers/forge!1470
2019-03-20 12:20:50 +00:00
Agetian
3e2fa43abe - Replace RemRandomDeck with the new format. 2019-03-20 15:20:14 +03:00
Michael Kamensky
e3ae076ab7 Merge branch 'master' into 'master'
Fix Saheeli crashing the AI.

See merge request core-developers/forge!1469
2019-03-20 12:15:24 +00:00
Agetian
64e09020bd - Fix Saheeli crashing the AI. 2019-03-20 15:14:36 +03:00
Sol
cd033aa896 Merge branch '908-mindbreak-trap-exiles-itself' into 'master'
Resolve "Mindbreak Trap Exiles itself"

Closes #908

See merge request core-developers/forge!1467
2019-03-20 12:02:21 +00:00
Hans Mackowiak
0e2222f97c Merge branch 'patch-1' into 'master'
Update gold.txt

Closes #913

See merge request core-developers/forge!1468
2019-03-20 05:33:53 +00:00
Sol
7629144be1 Update gold.txt 2019-03-20 01:11:02 +00:00
Hanmac
dece031e13 SpellAbilityEffect: fix getCards when only spell is targeted 2019-03-19 08:23:09 +01:00
swordshine
611813c141 Merge branch 'patch-2' into 'master'
Update monastery_mentor.txt

Closes #912

See merge request core-developers/forge!1466
2019-03-19 03:58:57 +00:00
Sol
fdc9aea650 Update monastery_mentor.txt 2019-03-19 02:41:39 +00:00
Sol
ccba38d944 Merge branch 'master' into 'master'
Fix to have "old edition images" of the "Elves vs Goblins" duel deck searched in the right folder "DD1" and not "EVG" (which is for the Anthology edition version of the same duel deck)

See merge request core-developers/forge!1465
2019-03-18 12:44:27 +00:00
Sol
def94171af Merge branch 'token_missing' into 'master'
Token: add missing TokenScript

See merge request core-developers/forge!1464
2019-03-17 23:08:12 +00:00
Hans Mackowiak
33e172ec37 Token: add missing TokenScript 2019-03-17 23:08:12 +00:00
Alessandro Coli
4f20cc638d Fix to have "old edition images" of this duel deck searched in the
folder "DD1", differently from the "anthology edition images" which are
searched into folder "EVG".

Without this fix, both image sets are searched into the same folder
"EVG".
2019-03-17 12:07:01 +01:00
Hans Mackowiak
93873d9984 Merge branch 'patch-2' into 'master'
Update c_1_1_eldrazi_scion_sac.txt

See merge request core-developers/forge!1463
2019-03-16 19:06:23 +00:00
Sol
e99e389221 Update c_1_1_eldrazi_scion_sac.txt 2019-03-16 18:29:33 +00:00
Sol
cc1f9763a7 Merge branch 'tokenscript_pt' into 'master'
TokenInfo: add TokenPower & TokenToughness into token script

See merge request core-developers/forge!1462
2019-03-16 18:26:47 +00:00
Hans Mackowiak
255a07fa2e TokenInfo: add TokenPower & TokenToughness into token script 2019-03-16 18:26:47 +00:00
Sol
b23b12ba66 Merge branch 'token-work' into 'master'
Token work

See merge request core-developers/forge!1263
2019-03-16 13:52:26 +00:00
Michael Kamensky
7ff9329b56 Merge branch 'master' into 'master'
Land sounds whenever they enter the battlefield - not only if they are played by hand

See merge request core-developers/forge!1459
2019-03-16 11:35:06 +00:00
Michael Kamensky
76e0d3d62f Merge branch 'patch-2' into 'master'
Update mesmerizing_benthid.txt

See merge request core-developers/forge!1461
2019-03-16 11:25:30 +00:00
Hans Mackowiak
888ad500a0 Update mesmerizing_benthid.txt 2019-03-16 11:25:30 +00:00
Michael Kamensky
4f11fdc54f Merge branch 'limit-tap-own-turn-ai' into 'master'
Be more conservative when AI uses Tap Effects

See merge request core-developers/forge!1460
2019-03-16 05:30:28 +00:00
Chris H
6e9c35a978 Be more conservative when AI uses Tap Effects 2019-03-15 23:29:30 -04:00
Alessandro Coli
f4b31082b8 Land sounds when land cards enter battlefield - even if they are not
played by hand
2019-03-15 20:34:22 +01:00
Alessandro Coli
9be9abbdae Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-03-15 20:18:45 +01:00
swordshine
3b059f9879 Merge branch 'patch-2' into 'master'
Some Card patches

See merge request core-developers/forge!1458
2019-03-15 11:28:53 +00:00
Hans Mackowiak
1ac44351c6 Some Card patches 2019-03-15 11:28:53 +00:00
Michael Kamensky
a7cc01ba65 Merge branch 'fix-lifeloseai' into 'master'
Fix LifeLoseAI not targeting for subabilities like Revenge.

Closes #906

See merge request core-developers/forge!1457
2019-03-15 04:15:25 +00:00
Agetian
bc75331809 - sa.isMandatory(): always false for subabilities, no reason to check. 2019-03-14 18:32:25 +03:00
Agetian
1ebde0d976 - Fix LifeLoseAI not targeting for subabilities like Revenge. 2019-03-14 16:56:23 +03:00
Alessandro Coli
99a6ad7288 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-03-13 20:29:50 +01:00
Hanmac
56c1a0e6d1 CardFactoryUtil: TokenScript for Fabricate and Living Weapon 2019-03-13 20:04:42 +01:00
schnautzr
341e80361d Finish token scripting for existing sets 2019-03-13 20:04:42 +01:00
schnautzr
d4206445c6 Add Duel Decks Anthologies, remove YMC from MTGO format. 2019-03-13 20:02:46 +01:00
schnautzr
508ea4d3d5 Convert cards to use new tokenscript format, minor fixes 2019-03-13 20:02:46 +01:00
Rob Schnautz
751900b376 Relocate two files that were misplaced 2019-03-13 08:25:26 +01:00
schnautzr
c43542cbe4 Finalize STH 2019-03-13 08:25:26 +01:00
schnautzr
7a68cc871f Finalize TMP 2019-03-13 08:24:33 +01:00
schnautzr
53443855b1 Finalize WTH 2019-03-13 08:24:33 +01:00
schnautzr
1faea98708 Finalize VAN series 1 2019-03-13 08:24:33 +01:00
schnautzr
13057386c9 Finalize VIS 2019-03-13 08:24:33 +01:00
schnautzr
b914422546 Finalize MIR 2019-03-13 08:24:33 +01:00
schnautzr
4240953950 Finalize ALL (alliances) 2019-03-13 08:24:33 +01:00
schnautzr
a306611853 Finalize HML 2019-03-13 08:24:33 +01:00
schnautzr
40acdd7f2d Finalize ICE 2019-03-13 08:23:57 +01:00
schnautzr
4c90e4c5fa Finalize FEM 2019-03-13 08:23:56 +01:00
schnautzr
8c096d6cd1 Finalize LEG. 2019-03-13 08:21:34 +01:00
schnautzr
31e9eeca05 Finalize ATQ 2019-03-13 08:17:57 +01:00
schnautzr
7afa975cd9 Finalize LEA and ARN tokens. 2019-03-13 08:15:15 +01:00
Rob Schnautz
09d5050766 Second pass. Fix errors, add alt art prints. 2019-03-13 08:06:11 +01:00
Rob Schnautz
54b866c58e guilds of ravnica tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
3d6ac9f432 magic 2019 tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
f0320bd567 dominaria tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
801a999986 rivals of ixalan tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
178e4a771b ixalan tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
1cf8ccb843 amonkhet block tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
007df1986d kaladesh block tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
ad50390e38 innistrad part 2 block tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
317634d0e7 zendikar part 2 block tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
fb356f88bc tarkir block tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
60275823fc theros block tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
f52d525b18 ravnica part 2 block tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
d10ce0b827 return to ravnica tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
3a6b925afd innistrad block tokens 2019-03-13 08:06:11 +01:00
Rob Schnautz
6f65baab38 mirrodin part two block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
7d294280a9 zendikar block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
d57100c888 alara block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
9d5d19f5c3 shards of alara tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
b6fe691a75 lorwyn block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
39b534ce2b time spiral block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
15b95ffc79 ravnica block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
c86307d44e kamigawa block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
4567c287d5 mirrodin block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
15032d7372 onslaught block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
7af24d52fd odyssey block tokens 2019-03-13 08:06:10 +01:00
Rob Schnautz
fac6e7b689 invasion block tokens 2019-03-13 08:06:09 +01:00
Rob Schnautz
ea33430706 masques block tokens 2019-03-13 08:06:09 +01:00
Rob Schnautz
5a5c7f96cb Urza's block tokens 2019-03-13 08:06:09 +01:00
Rob Schnautz
cbe8407b28 Tempest block tokens 2019-03-13 08:06:09 +01:00
Rob Schnautz
724f1ce10a Mirage block tokens 2019-03-13 08:06:09 +01:00
Rob Schnautz
e1ccedf881 pre-mirage tokens in edition files 2019-03-13 08:06:09 +01:00
Hans Mackowiak
cb7af23c13 Update khalni_garden.txt 2019-03-13 08:04:04 +01:00
Hans Mackowiak
e2c4c52bd7 Update kuldotha_rebirth.txt 2019-03-13 08:04:04 +01:00
Hans Mackowiak
305d7b69aa Update survey_the_wreckage.txt 2019-03-13 08:04:04 +01:00
schnautzr
bb10e6e604 delete duplicate scripts with wrong names 2019-03-13 08:04:04 +01:00
schnautzr
6ca9bd2926 Fix Ragavan 2019-03-13 08:04:04 +01:00
Rob Schnautz
29102dcab3 rest of urza's tokens 2019-03-13 07:43:58 +01:00
Rob Schnautz
73f8fa1ac8 urza's saga tokens 2019-03-13 07:43:58 +01:00
Rob Schnautz
3f75adcfae exodus tokens 2019-03-13 07:42:32 +01:00
Rob Schnautz
1707e34860 stronghold tokens 2019-03-13 07:41:37 +01:00
Rob Schnautz
a43019fcee tempest tokens 2019-03-13 07:38:44 +01:00
Rob Schnautz
812a5c873f weatherlight tokens 2019-03-13 07:37:43 +01:00
Rob Schnautz
73a4aa7461 visions tokens 2019-03-13 07:37:42 +01:00
Rob Schnautz
6a1dc16b52 mirage tokens 2019-03-13 07:36:59 +01:00
Rob Schnautz
409499d9d9 alliances tokens 2019-03-13 07:35:20 +01:00
Rob Schnautz
947ebb0f0b fallen empires tokens 2019-03-13 07:34:01 +01:00
Rob Schnautz
7baab5786b ice age tokens 2019-03-13 07:31:59 +01:00
Rob Schnautz
1dfd9ac340 fallen empires tokens 2019-03-13 07:31:58 +01:00
Rob Schnautz
c4fad2f2fa serpent generator 2019-03-13 07:30:44 +01:00
Rob Schnautz
ddcf0283a9 Legends tokens 2019-03-13 07:30:44 +01:00
Rob Schnautz
c0c2317adb tetravite 2019-03-13 07:30:43 +01:00
Rob Schnautz
f87107751f add Djinn token for Bottle of Suleiman 2019-03-13 07:29:56 +01:00
Rob Schnautz
a22fd58fbc token for Rukh Egg 2019-03-13 07:29:56 +01:00
Rob Schnautz
85b904ca65 add goblin and wasp to all editions that create em 2019-03-13 07:29:56 +01:00
Rob Schnautz
3258d9a5fa wasp: the original token 2019-03-13 07:29:56 +01:00
Rob Schnautz
846a30454d g_0_1_plant 2019-03-13 07:29:56 +01:00
Rob Schnautz
fbda01ef43 all the r_1_1_goblins 2019-03-13 07:28:19 +01:00
Michael Kamensky
586e502780 Merge branch 'master' into 'master'
Added puzzle PS_RNA7 (Possibility Storm - Ravnica Allegiance  07).

See merge request core-developers/forge!1456
2019-03-12 06:10:40 +00:00
Agetian
688f8599be - Added puzzle PS_RNA7. 2019-03-12 09:09:48 +03:00
Agetian
c161cdcf51 - Fix the name of MPS_RNA edition. 2019-03-12 09:00:01 +03:00
Michael Kamensky
2937041c63 Merge branch 'card-and-image-audit-menu' into 'master'
Card and image audit menu

See merge request core-developers/forge!1451
2019-03-12 05:51:21 +00:00
Tim Scott
eafa4418d6 Reduce the size of the font for the audit report.
Change up the scroll window to allow for horizontal movement.

Add code/code2 info for easier reference.
2019-03-10 15:39:10 -05:00
Michael Kamensky
2ff19e05a1 Merge branch 'master' into 'master'
Forge for Android v1.6.22.001

See merge request core-developers/forge!1455
2019-03-10 15:31:57 +00:00
Agetian
e23acfb5de - Preparing Forge for Android publish 1.6.22.001 [incremental]. 2019-03-10 18:29:36 +03:00
Agetian
255c30ce9c Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-03-10 17:54:00 +03:00
Blacksmith
49ba10d97b Clear out release files in preparation for next release 2019-03-10 14:02:21 +00:00
Blacksmith
c3346c737e [maven-release-plugin] prepare for next development iteration 2019-03-10 13:57:44 +00:00
Blacksmith
db3c4d2d2f [maven-release-plugin] prepare release forge-1.6.22 2019-03-10 13:57:39 +00:00
Blacksmith
c0c38dbef9 Update README.txt for release 2019-03-10 13:55:41 +00:00
Sol
5fb20c7eb9 Merge branch 'patch-3' into 'master'
Patch 3

See merge request core-developers/forge!1454
2019-03-10 13:54:14 +00:00
Sol
1791d96a78 Merge branch 'patch-2' into 'master'
Update dovin_architect_of_law.txt

See merge request core-developers/forge!1453
2019-03-10 13:54:01 +00:00
Tim Scott
89df7694d9 Correct copy to clipboard action. 2019-03-10 08:51:51 -05:00
Sol
c2690a80e2 Update ANNOUNCEMENTS.txt 2019-03-10 13:51:43 +00:00
Sol
b50779177e Update CHANGES.txt 2019-03-10 13:49:00 +00:00
Sol
b5fb772893 Update dovin_architect_of_law.txt 2019-03-10 13:35:05 +00:00
Michael Kamensky
341d882a93 Merge branch 'patch-1' into 'master'
Update domri_city_smasher.txt

See merge request core-developers/forge!1452
2019-03-10 08:36:40 +00:00
Sol
1ea8519be8 Update domri_city_smasher.txt 2019-03-10 04:06:42 +00:00
Tim Scott
02abe851c0 Organize images not found and cards not implemented into two separate lists for easier reference.
Add label to dialog after audit operation for easier reference.
2019-03-09 12:41:17 -06:00
Agetian
c73ed59a52 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-03-09 17:36:32 +03:00
Michael Kamensky
10b68ae716 Merge branch '897-mayplay-allows-facedown-land-even-it-shoudn-t' into 'master'
Resolve "MayPlay allows Facedown Land even it shoudn't"

Closes #897

See merge request core-developers/forge!1450
2019-03-09 14:36:01 +00:00
Agetian
e99a507744 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-03-09 16:50:21 +03:00
Hanmac
393207c9d8 Card: fixed LandAbility + FaceDown + MayPlay 2019-03-09 13:59:21 +01:00
Alessandro Coli
facb85f74f Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-03-09 13:34:44 +01:00
Michael Kamensky
c0103815be Merge branch 'clash-log-post-action' into 'master'
Log after clashing so players know what happened to top of library

See merge request core-developers/forge!1449
2019-03-09 03:53:17 +00:00
tehdiplomat
f7d40687b6 Log after clashing so players know what happened to top of library 2019-03-08 10:11:15 -05:00
Michael Kamensky
a24a844078 Merge branch 'master' into 'master'
Fix the AI not considering Adapt P/T bonus correctly on attack

Closes #892

See merge request core-developers/forge!1448
2019-03-08 09:52:13 +00:00
Michael Kamensky
a820ba35f7 Fix the AI not considering Adapt P/T bonus correctly on attack 2019-03-08 09:52:13 +00:00
Agetian
e4bd18e25c - Additional fix for the other stat. 2019-03-08 12:50:15 +03:00
Agetian
efd9b6a0fc - Fix a bug with the AI not considering Adapt correctly during attack. 2019-03-08 12:49:06 +03:00
Sol
baa4f8cd74 Merge branch '894-dies-leaves-triggers-don-t-work-on-copied-permanents' into 'master'
Resolve "Dies/Leaves triggers don't work on copied permanents"

Closes #894

See merge request core-developers/forge!1447
2019-03-08 02:32:58 +00:00
Hanmac
9067d676eb GameAction: register from LKI to fix leave the battlefield 2019-03-07 20:34:32 +01:00
Michael Kamensky
4b6cf610a7 Merge branch 'master' into 'master'
Added puzzle PS_RNA6 (Possibility Storm - Ravnica Allegiance 06)

See merge request core-developers/forge!1446
2019-03-06 17:58:41 +00:00
Agetian
12ee419158 - Fix PS_RNA6. 2019-03-06 20:57:52 +03:00
Michael Kamensky
53075e5099 Merge branch 'master' into 'master'
Sound support for multicolor lands

See merge request core-developers/forge!1445
2019-03-06 17:56:56 +00:00
Agetian
33ee5a0a17 - Added puzzle PS_RNA6. 2019-03-06 20:54:06 +03:00
Alessandro Coli
0bd29eb1e7 Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git 2019-03-05 22:52:20 +01:00
Alessandro Coli
c4d859cb03 Sound support for multicolor lands 2019-03-05 19:17:48 +01:00
Michael Kamensky
0cbd22ae69 Merge branch 'estrid-fix' into 'master'
Fix Estrid, the Masked -1 ability

See merge request core-developers/forge!1443
2019-03-05 11:56:51 +00:00
Michael Kamensky
05ecffcbd1 Merge branch 'master' into 'master'
Forge for Android 1.6.21.003

See merge request core-developers/forge!1444
2019-03-05 06:13:19 +00:00
Agetian
3f15be2732 - Fix inverse logic in TokenEffect (fixes e.g. Estrid, the Masked -1 ability) 2019-03-05 09:10:48 +03:00
Agetian
cc13020b4b - Preparing Forge for Android publish 1.6.21.003 [hotfix]. 2019-03-05 08:35:36 +03:00
Agetian
1a27142f1f Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-03-05 08:02:13 +03:00
Sol
89348173a5 Merge branch 'revert-fc987a81' into 'master'
Revert "Merge branch 'fix-bad-art-deck-art-corruption' into 'master'"

See merge request core-developers/forge!1442
2019-03-04 15:58:17 +00:00
Sol
8a9c27f9ae Revert "Merge branch 'fix-bad-art-deck-art-corruption' into 'master'"
This reverts merge request !1440
2019-03-04 15:58:06 +00:00
Agetian
7da2316c0a - Preparing Forge for Android publish 1.6.21.002 [incremental]. 2019-03-04 16:34:33 +03:00
Michael Kamensky
6807257630 Merge branch 'custom-pool-options' into 'master'
Swanky new Choose Sets Dialog randomizer

See merge request core-developers/forge!1431
2019-03-04 13:13:50 +00:00
Michael Kamensky
223136dd6f Merge branch 'android-max-size-image-cache' into 'master'
Android max size image cache

See merge request core-developers/forge!1432
2019-03-04 13:13:35 +00:00
Sol
0b997a2937 Android max size image cache 2019-03-04 13:13:35 +00:00
Michael Kamensky
66debd6806 Merge branch 'master' into 'master'
Added puzzle PS_RNA5 (Possibility Storm - Ravnica Allegiance 05)

See merge request core-developers/forge!1441
2019-03-04 13:13:10 +00:00
Agetian
063ebf2f70 - Added puzzle PS_RNA5. 2019-03-04 15:04:21 +03:00
Michael Kamensky
940cd71d34 Merge branch 'master' into 'master'
ManaBurn sound working again + Edition files for Mythic Edition of Guild of Ravnica and Ravnica Allegiance

See merge request core-developers/forge!1437
2019-03-04 03:22:56 +00:00
Michael Kamensky
fc987a81a0 Merge branch 'fix-bad-art-deck-art-corruption' into 'master'
If a DeckList references a bogus card set/art combo ignore parts of it until it works

Closes #132

See merge request core-developers/forge!1440
2019-03-04 03:22:44 +00:00
Chris H
888f5953b1 If a DeckList references a bogus card set/art combo ignore parts of it until it works 2019-03-03 21:47:28 -05:00
Sol
c30086390d Merge branch 'master' into 'master'
Moving hard-coded text to en-US.properties file

See merge request core-developers/forge!1413
2019-03-03 20:44:33 +00:00
Sol
37207559e6 Merge branch 'patch-2' into 'master'
Update Bug.md

See merge request core-developers/forge!1439
2019-03-03 17:47:25 +00:00
Sol
93bd90f139 Update Bug.md 2019-03-03 17:47:01 +00:00
Tim Scott
e67b55d3cf Add ability to audit cards and card image data.
README version info fixup.
2019-03-03 10:00:10 -06:00
Alessandro Coli
df55bc5d34 Fixing of tabs as required 2019-03-03 10:13:22 +01:00
Sol
ce7b66652c Merge branch 'add-gitlab-templates' into 'master'
Add gitlab templates

See merge request core-developers/forge!1438
2019-03-03 02:23:03 +00:00
Chris H
c091eefc84 Add gitlab templates 2019-03-02 21:20:47 -05:00
Alessandro
b7508b8c3a ManaBurn sound working again 2019-03-02 16:10:13 +01:00
Alessandro
b5a0e15327 Merge remote-tracking branch 'upstream/master' 2019-03-02 13:14:33 +01:00
Alessandro
d29ed9a5fe New editions files for "Mythic editions" of Guilds of Ravnica and
Ravnica Allegiance
2019-03-02 12:45:15 +01:00
Michael Kamensky
62cf8533e0 Merge branch 'ai-damage-crash' into 'master'
Ai damage crash

See merge request core-developers/forge!1434
2019-03-01 18:59:07 +00:00
Chris H
7a96ff2d4b Verify AI divided damage always fills out divided hash with proper values 2019-02-28 22:16:44 -05:00
Sol
cafd7d9c64 Merge branch 'quest-load-npe-fix' into 'master'
Fix NPE when no quests are available to load

See merge request core-developers/forge!1433
2019-02-28 02:52:49 +00:00
KrazyTheFox
5377376585 Fix NPE when no quests are available to load 2019-02-27 21:49:31 -05:00
Chris H
69661cab63 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-27 21:36:32 -05:00
Chris H
15520fcc67 Merge branch 'master' of https://git.cardforge.org/friarsol/forge 2019-02-27 21:35:55 -05:00
KrazyTheFox
eedbdeca99 Add release notes 2019-02-27 20:55:37 -05:00
Churrufli
34380befaf Update AddBasicLandsDialog.java 2019-02-27 19:36:16 +00:00
churrufli
da5b1a18bf Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-27 20:13:44 +01:00
churrufli
a546339ea2 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-27 19:58:25 +01:00
churrufli
558442a6bb Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-27 17:35:06 +01:00
Michael Kamensky
16c9cc7eac Merge branch 'lq-image-download-tweak' into 'master'
Update server download URL parsing

Closes #858

See merge request core-developers/forge!1421
2019-02-27 12:22:14 +00:00
Michael Kamensky
71da9ca610 Merge branch 'patch-1' into 'master'
Remove unnecessary RNA token image URLs

See merge request core-developers/forge!1427
2019-02-27 12:21:46 +00:00
Michael Kamensky
d54ca72994 Merge branch 'deck-editor-filtering-tweaks' into 'master'
Fix deck editor Set/Format edit problems.

Closes #863 and #855

See merge request core-developers/forge!1420
2019-02-27 12:21:29 +00:00
Michael Kamensky
79ce24b682 Merge branch 'patch' into 'master'
Update Lavinia, Azorius Renegade for multiplayer

See merge request core-developers/forge!1422
2019-02-27 12:21:17 +00:00
Michael Kamensky
0662dc5964 Merge branch 'set-max-size-for-image-cache' into 'master'
Set max size for image cache (Reduces memory footprint)

See merge request core-developers/forge!1423
2019-02-27 12:19:52 +00:00
Sol
e7f7e62cbf Remove unnecessary RNA token image URLs 2019-02-27 02:48:05 +00:00
KrazyTheFox
17c280f052 Merge branch 'master' of git.cardforge.org:core-developers/forge into custom-pool-options 2019-02-26 21:17:10 -05:00
swordshine
8d3fe2088a Merge branch 'gk2' into 'master'
GK2 edition

See merge request core-developers/forge!1424
2019-02-27 01:21:20 +00:00
swordshine
a87adfaf16 Merge branch 'type-grouping-patch' into 'master'
Sort Sorcery // Instant as an instant in Type sort.

See merge request core-developers/forge!1426
2019-02-27 01:20:45 +00:00
swordshine
ed895a5248 Merge branch 'oracle-nonfunctional' into 'master'
Oracle nonfunctional

See merge request core-developers/forge!1425
2019-02-27 01:19:51 +00:00
Rob Schnautz
143a254189 Sort Sorcery // Instant as an instant in Type sort. 2019-02-27 00:31:05 +00:00
Rob Schnautz
25a3367d74 Let's do the Wurm Warp 2019-02-27 00:01:12 +00:00
Rob Schnautz
400c7d366b any time 2019-02-26 23:58:20 +00:00
Rob Schnautz
43cdbbdbee Trample reminder text. 2019-02-26 23:48:51 +00:00
Rob Schnautz
bddba4c6dc Update trample reminder text 2019-02-26 23:43:14 +00:00
Rob Schnautz
4ab79ce75d fix date 2019-02-26 22:57:38 +00:00
Rob Schnautz
42eba01a76 GK2 edition 2019-02-26 22:45:23 +00:00
tehdiplomat
f0efc2089c Ad LRU to ImageCache to get a handle on memory bloat 2019-02-26 15:40:36 -05:00
tehdiplomat
3095c25f6d Merge branch 'master' of https://git.cardforge.org/friarsol/forge 2019-02-26 15:40:16 -05:00
swordshine
f38ec8d4a0 - Update Lavinia, Azorius Renegade for multiplayer 2019-02-26 16:59:25 +08:00
churrufli
56b3f04120 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-25 11:36:50 +01:00
Michael Kamensky
e35978594a Merge branch 'mobile-gui-zoom-update' into 'master'
Mobile gui zoom update

Closes #845

See merge request core-developers/forge!1418
2019-02-25 10:25:57 +00:00
Tim Scott
99aab2e716 Correct typo.
Add SNAPSHOT build info for Eclipse.
2019-02-24 22:31:59 -06:00
Tim Scott
17df25399f Use pattern matching to capture directory listing from the server URL string.
Add fallback to PNG for download if JPG is not found.

Correct quest-opponent-icons.txt.  Space reference should be "%20".
2019-02-24 17:22:28 -06:00
churrufli
405e07a78e Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-25 00:02:01 +01:00
Tim Scott
b090cdfa41 Fix deck editor Set/Format edit problems.
Add code to respect the "allow reprints" option on the Formats dialog.  Default it to 'true'.  Also add code to be able to initialize the sate of  that same checkbox.

Change format and set edits to refresh the item manager list so edits are realized.

Change wording of set "allow reprints" checkbox to remove confusion / conflict with the format "allow reprints" checkbox.

Add constructor so a whole format list can simply be added at once rather than iterating and creating multiple filter objects for each format.  (Net result is the same in either case.)

Change text of filter drop down to indicate things are being added or edited vs just added.
2019-02-24 12:33:58 -06:00
Michael Kamensky
8c810a120e Merge branch 'fixTriggerStatic' into 'master'
Fix trigger static

See merge request core-developers/forge!1417
2019-02-24 15:11:05 +00:00
Hanmac
fe4ce14a4b TriggerChangesZone: this check only for nonStatic Trigger 2019-02-24 15:59:36 +01:00
Tim Scott
ae8271de01 Add missing Eclipse steps to README for building for Android target. 2019-02-23 16:42:27 -06:00
Tim Scott
22aa98bf92 Add logic to prevent overzoom of rotated split cards for forge-gui-mobile. 2019-02-23 16:31:08 -06:00
Sol
f5f164af93 Merge branch 'unmappable-characters' into 'master'
Replace unmappable characters.

See merge request core-developers/forge!1416
2019-02-23 21:04:11 +00:00
churrufli
48216f95e1 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-23 20:25:57 +01:00
churrufli
6ef9f4bc7b undo translation in AddBasicLandsDialog.java 2019-02-23 19:47:52 +01:00
schnautzr
4ddf811118 Replace unmappable characters. 2019-02-23 11:37:28 -06:00
Michael Kamensky
3cf8b956e4 Merge branch '846-transform-with-die-trigger' into 'master'
Resolve "Transform with die trigger"

Closes #846

See merge request core-developers/forge!1408
2019-02-23 15:14:43 +00:00
Hanmac
9bf908ccb4 GameAction: fix die trigger for non-Original
TriggerChangesZone: fix leave battlefield on hidden
2019-02-23 14:50:08 +01:00
Michael Kamensky
38d3f76bb5 Merge branch 'fixTokenScriptAgain' into 'master'
TokenInfo: fix if script is null

See merge request core-developers/forge!1415
2019-02-23 13:44:07 +00:00
Hanmac
0c2cfdce34 TokenInfo: fix if script is null 2019-02-23 13:47:40 +01:00
Michael Kamensky
ea2b61cf5b Merge branch 'fix-ai-attack-token-round2' into 'master'
Fix ai attack token round2

See merge request core-developers/forge!1414
2019-02-23 05:07:11 +00:00
Chris H
ffda74101f Actually fix the crash in TokenAI.spawnToken for Attacking tokens 2019-02-22 21:50:35 -05:00
Chris H
08f47d58a1 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-22 19:55:26 -05:00
churrufli
f87f6bdebe undo translation in AddBasicLandsDialog.java 2019-02-22 17:35:37 +01:00
churrufli
2e70ab329e undo translation in AddBasicLandsDialog.java 2019-02-22 17:26:44 +01:00
churrufli
73749f28da Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-22 17:23:31 +01:00
churrufli
f7134f0a54 Translating hard-coded text to res en-US.properties 2019-02-22 16:53:57 +01:00
churrufli
231b2da573 Translating hard-coded text to res en-US.properties 2019-02-22 15:28:02 +01:00
churrufli
bf5c319bfe Merge remote-tracking branch 'origin/master' 2019-02-22 15:11:44 +01:00
churrufli
d781b6ba61 Translating hard-coded text to res en-US.properties 2019-02-22 15:10:54 +01:00
Michael Kamensky
98282acc60 Merge branch 'forums-bug-fix-attach-ai' into 'master'
Fix the thing Sol told me to

See merge request core-developers/forge!1412
2019-02-22 13:06:33 +00:00
Sol
d2917f0e43 Merge branch '850-untilyournextturn-incorrectly-ends-during-your-next-cleanup-phase' into 'master'
Resolve "UntilYourNextTurn incorrectly ends during your next cleanup phase"

Closes #850

See merge request core-developers/forge!1409
2019-02-22 12:58:14 +00:00
swordshine
919aecd9a1 Merge branch 'timmermac-sting-easy' into 'master'
Add Sting deck by timmermac

See merge request core-developers/forge!1345
2019-02-22 12:39:32 +00:00
swordshine
3c17b22a95 Merge branch 'cherry-pick-7d6a2618' into 'master'
Make sure Token AI loads TokenScript params before doing things with hte token

See merge request core-developers/forge!1411
2019-02-22 12:38:20 +00:00
Churrufli
da9373a0df Merge branch 'master' into 'master'
Translating hard-coded text to res en-US.properties

See merge request core-developers/forge!1410
2019-02-22 06:51:50 +00:00
Churrufli
3631b8be75 Update en-US.properties 2019-02-22 06:46:32 +00:00
KrazyTheFox
63ec3aae87 Fix the thing Sol told me to 2019-02-21 23:48:38 -05:00
Chris H
46670d50f3 Make sure Token AI loads TokenScript params before doing things with hte token
(cherry picked from commit 7d6a26184b4f852442730237826c94a16d739e5b)
2019-02-22 04:19:55 +00:00
KrazyTheFox
1416b668a3 Add Sting deck description 2019-02-21 21:12:38 -05:00
Krazy
893cc3b08f Merge branch 'eclipse-cleanup' into 'master'
Eclipse cleanup

Closes #844

See merge request core-developers/forge!1399
2019-02-22 02:09:05 +00:00
churrufli
25858f625b Translating hard-coded text to res en-US.properties 2019-02-22 00:30:48 +01:00
Hanmac
9e6d9e8e30 PumpEffect: fixed Until Parameter for Player 2019-02-21 16:19:12 +01:00
Michael Kamensky
1244ac89fd Merge branch 'master' into 'master'
Added puzzle PS_RNA4 - Possibility Storm: Ravnica Allegiance 04

See merge request core-developers/forge!1407
2019-02-20 05:01:53 +00:00
Agetian
facf22c092 - Added puzzle PS_RNA4. 2019-02-20 08:01:02 +03:00
Michael Kamensky
d1895ef97e Merge branch 'patch-1' into 'master'
Add m19 gift pack to Standard formats

See merge request core-developers/forge!1403
2019-02-20 03:50:43 +00:00
Michael Kamensky
0575cd98d9 Merge branch 'patch-2' into 'master'
Fix NPE in QuestController.java

See merge request core-developers/forge!1406
2019-02-20 03:50:32 +00:00
Sol
2f466834f9 Update QuestController.java 2019-02-20 01:56:13 +00:00
Sol
3282ccf6b1 Fix NPE in QuestController.java 2019-02-20 01:49:59 +00:00
Sol
6ae7cddcb9 Merge branch 'master' into 'master'
fix crash when showing uninitialized zone

See merge request core-developers/forge!1405
2019-02-19 16:10:52 +00:00
Peter F. Patel-Schneider
0d79a84b9f tiny optimization 2019-02-19 10:51:41 -05:00
Peter F. Patel-Schneider
6fcb996938 fix crash when showing uninitialized zone 2019-02-19 08:10:09 -05:00
Sol
27651313a7 Update Modern.txt 2019-02-19 00:33:57 +00:00
Sol
9d762b5740 Merge branch 'master' into 'master'
Update consecrate_consume.txt

See merge request core-developers/forge!1404
2019-02-19 00:32:24 +00:00
T.J. Tillman
e51afbc21c Update consecrate_consume.txt 2019-02-19 00:06:21 +00:00
Chris H
0ca3378901 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-18 13:52:06 -05:00
Sol
b544718140 Merge branch 'master' into 'master'
Use PeekAndReveal for Sunbird's Invocation

Closes #841

See merge request core-developers/forge!1395
2019-02-18 16:47:42 +00:00
Sol
dc4b65593e Update Brawl.txt 2019-02-18 16:00:23 +00:00
Sol
5f5b34478d Update Standard.txt 2019-02-18 15:59:02 +00:00
Sol
64e7aa902b Update M19 Gift Pack.txt 2019-02-18 15:58:32 +00:00
tehdiplomat
f6f36b75d9 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-17 22:43:39 -05:00
Chris H
99b761082e Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-17 21:33:58 -05:00
Sol
29d917d8f5 Merge branch 'master' into 'master'
preparing android build

See merge request core-developers/forge!1402
2019-02-17 23:52:10 +00:00
Jamin W. Collins
63abdd6d00 preparing android build
Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
2019-02-17 14:09:54 -07:00
Blacksmith
e4d41001db Clear out release files in preparation for next release 2019-02-17 19:56:08 +00:00
Blacksmith
4ed17e1e16 [maven-release-plugin] prepare for next development iteration 2019-02-17 19:51:17 +00:00
Blacksmith
a70136c98c [maven-release-plugin] prepare release forge-1.6.21 2019-02-17 19:51:13 +00:00
Blacksmith
e42f3f3369 Update README.txt for release 2019-02-17 19:48:59 +00:00
Hans Mackowiak
6fc53b8fe4 Merge branch 'fix-token-images' into 'master'
Fix token images

See merge request core-developers/forge!1401
2019-02-16 14:04:45 +00:00
Sol
557be177d8 Fix token images 2019-02-16 14:04:45 +00:00
Hans Mackowiak
3fab25ab8c Merge branch 'master' into 'master'
Clarify Target Prompts for Applied Biomancy and Swirling Torrent

See merge request core-developers/forge!1398
2019-02-16 13:13:09 +00:00
Tim Scott
3be4c7e379 Update README.md with corrections regarding Eclipse build steps. 2019-02-15 21:12:29 -06:00
T.J. Tillman
452a47e573 Update swirling_torrent.txt 2019-02-16 02:42:27 +00:00
T.J. Tillman
d002ddaa5d Update applied_biomancy.txt 2019-02-16 02:42:06 +00:00
Sol
95a69f053b Merge branch 'deckconstructionrules-null-check' into 'master'
Add null check for getDeckConstructionRules()

See merge request core-developers/forge!1397
2019-02-16 01:37:35 +00:00
Chris H
f2c48a386b Add null check for getDeckConstructionRules() 2019-02-15 20:28:40 -05:00
Sol
7db1b4a106 Merge branch 'master' into 'master'
Update flames_of_the_raze_boar.txt

See merge request core-developers/forge!1396
2019-02-16 01:26:52 +00:00
T.J. Tillman
8b186bb890 Update flames_of_the_raze_boar.txt 2019-02-15 19:45:35 +00:00
Peter F. Patel-Schneider
2b0fc5a00a generalize card showing in PlayEffect 2019-02-15 10:59:12 -05:00
Peter F. Patel-Schneider
f63e59fbda PlayEffect has ShowRemembered parameter which shows remembered cards if playing cards selected from a zone 2019-02-15 08:49:16 -05:00
Tim Scott
cd044f1532 Update README with Eclipse setup and build info. 2019-02-14 22:20:47 -06:00
Tim Scott
7f16a3dce2 Remove Eclipse-specific files and directories. Maven-based build configurations can be easily imported by (at least) Eclipse 2018-12. Hint: File > Import > Maven > Existing Maven Projects > Navigate to root forge folder.
Tweak base pom.xml to allow for error-free Eclipse compilation.
2019-02-14 21:26:23 -06:00
Peter F. Patel-Schneider
3d3f1b08e4 prevent shuffling during resolution of Sunbird's Invocation and Aetherworks Marvel 2019-02-14 19:24:55 -05:00
Peter F. Patel-Schneider
f4c326826e Use PeekAndReveal for Sunbird's Invocation 2019-02-14 16:07:40 -05:00
Michael Kamensky
bf0e0a94e4 Merge branch 'master' into 'master'
fix digging with change number of 0

See merge request core-developers/forge!1394
2019-02-14 16:32:22 +00:00
Peter F. Patel-Schneider
3a71e2e948 fix digging with change number of 0 2019-02-14 11:04:11 -05:00
Sol
a739492591 Merge branch 'master' into 'master'
Update lavinia_azorius_renegade.txt

See merge request core-developers/forge!1393
2019-02-14 04:04:10 +00:00
T.J. Tillman
7a14109abc Update lavinia_azorius_renegade.txt 2019-02-14 01:15:58 +00:00
Michael Kamensky
708886e8cf Merge branch 'master' into 'master'
Add several APIs to the timestamp check in Wrapped Ability, fixes e.g. Toothy LTB trigger.

Closes #839

See merge request core-developers/forge!1392
2019-02-13 07:05:46 +00:00
Agetian
ab56e1f7d4 - Add several APIs to the timestamp check in Wrapped Ability, fixes e.g. Toothy LTB trigger. 2019-02-13 10:05:05 +03:00
swordshine
038bc27074 Merge branch 'patch-1' into 'master'
Update ragavan.txt

See merge request core-developers/forge!1391
2019-02-13 02:42:06 +00:00
Sol
4cc397baa4 Update ragavan.txt 2019-02-13 02:11:13 +00:00
Michael Kamensky
398b2ee324 Merge branch 'splitcmc' into 'master'
Fix for CMC of split cards on the stack

Closes #538

See merge request core-developers/forge!1318
2019-02-12 03:59:07 +00:00
Evan Murawski
6f85b09ee4 Fix for CMC of split cards on the stack 2019-02-12 03:59:07 +00:00
Michael Kamensky
00f6445049 Merge branch 'master' into 'master'
Added puzzle PS_RNA3.

See merge request core-developers/forge!1390
2019-02-12 03:53:34 +00:00
Agetian
24d857f269 - Added puzzle PS_RNA3. 2019-02-12 06:52:48 +03:00
Michael Kamensky
cb13f0a685 Merge branch 'master' into 'master'
allow floating zones to be sorted by the name of the card

See merge request core-developers/forge!1387
2019-02-12 03:35:49 +00:00
Peter F. Patel-Schneider
e8f14d5a6c describe change in CHANGES.txt; comment cleanup 2019-02-11 08:04:36 -05:00
Michael Kamensky
64c9f7ffb3 Merge branch '832-state-based-actions-check-conflict-with-upkeep-trigger' into 'master'
Resolve "State Based Actions check conflict with Upkeep trigger"

Closes #832

See merge request core-developers/forge!1380
2019-02-11 13:00:14 +00:00
Michael Kamensky
899532666e Merge branch 'master' into 'master'
Resolve Angel of Grace's Graveyard Ability crash

Closes #838

See merge request core-developers/forge!1388
2019-02-11 12:35:57 +00:00
Michael Kamensky
d76cd0e37d Merge branch '836-epochrasite' into 'master'
Resolve "Epochrasite"

Closes #836

See merge request core-developers/forge!1384
2019-02-11 12:35:18 +00:00
Michael Kamensky
749dc79775 Merge branch '837-rakdos-roustabout-doesn-t-deal-damage-from-his-trigger' into 'master'
Resolve "Rakdos Roustabout doesn't deal damage from his trigger"

Closes #837

See merge request core-developers/forge!1385
2019-02-11 12:35:05 +00:00
T.J. Tillman
eb17f4972a Update angel_of_grace.txt 2019-02-10 19:06:10 +00:00
Peter F. Patel-Schneider
c9f9fd93cd allow floating zones to be sorted by the name of the card 2019-02-10 13:28:52 -05:00
Hans Mackowiak
e83c78e2b4 Epochrasite: the LKI thing there is wrong 2019-02-10 11:31:50 +01:00
Michael Kamensky
bf7f07b432 Merge branch 'master' into 'master'
Fix for Clear the Stage

See merge request core-developers/forge!1386
2019-02-10 10:22:24 +00:00
T.J. Tillman
245994d094 Update clear_the_stage.txt 2019-02-10 09:40:13 +00:00
Michael Kamensky
c055e4d89c Merge branch 'patch-4' into 'master'
Update angelic_exaltation.txt

See merge request core-developers/forge!1383
2019-02-10 07:00:12 +00:00
Hanmac
fcdf49bafd TriggerAttackerBlocked: add TriggeredDefender 2019-02-10 07:42:18 +01:00
T.J. Tillman
b56472555a Revert "Update TriggerAttackerBlocked.java"
This reverts commit 06df7cb036
2019-02-10 05:53:23 +00:00
T.J. Tillman
06df7cb036 Update TriggerAttackerBlocked.java 2019-02-10 05:51:08 +00:00
Sol
d6b011fd72 Update angelic_exaltation.txt 2019-02-10 03:11:26 +00:00
Michael Kamensky
b853f34c9e Merge branch 'auraswapKeyword' into 'master'
CardFactoryUtil: Aura swap as Keyword

See merge request core-developers/forge!1382
2019-02-09 17:48:52 +00:00
Hanmac
4da05f03dc CardFactoryUtil: Aura swap as Keyword 2019-02-09 17:53:05 +01:00
Michael Kamensky
52cc02a316 Merge branch 'mayPlayFix' into 'master'
Spell: Fixed MayPlay, getRestrictions.canPlay need originalHost

Closes #827

See merge request core-developers/forge!1381
2019-02-09 14:09:52 +00:00
Hanmac
d188708028 Spell: Fixed MayPlay, getRestrictions.canPlay need originalHost 2019-02-09 09:29:32 +01:00
Michael Kamensky
1e39137ab5 Merge branch 'patch-1' into 'master'
Deputy of Detention: need Condition like Bishop of Binding

See merge request core-developers/forge!1376
2019-02-09 03:59:13 +00:00
Hanmac
47c89fb0ae PhaseHandler: fix executeUntil needs to be done after checkStateEffects 2019-02-08 19:51:51 +01:00
Michael Kamensky
cf57832970 Merge branch 'patch-3' into 'master'
Update Modern.txt

See merge request core-developers/forge!1379
2019-02-08 18:12:22 +00:00
Michael Kamensky
6c1487a491 Merge branch 'patch-2' into 'master'
Update Brawl.txt

See merge request core-developers/forge!1378
2019-02-08 18:12:08 +00:00
Michael Kamensky
6f26becf5c Merge branch 'ai-combat-adapt' into 'master'
Implement a simple combat Adapt logic.

Closes #831

See merge request core-developers/forge!1377
2019-02-08 17:33:31 +00:00
Agetian
0a543ba35e - Don't try the Adapt logic if the card can't receive +1/+1 counters. 2019-02-08 20:17:36 +03:00
Sol
09f90bfe35 Update Modern.txt 2019-02-08 16:13:58 +00:00
Sol
0c06346ca0 Update Brawl.txt 2019-02-08 16:12:15 +00:00
Agetian
1df44f8166 - Implement a simple combat Adapt logic. 2019-02-08 18:05:54 +03:00
Michael Kamensky
0d67c0fb9e Merge branch 'android-npe-fix' into 'master'
Add test for model being null.  This will occur upon deleting a deck. ...

See merge request core-developers/forge!1375
2019-02-08 11:47:03 +00:00
Hans Mackowiak
5885c37a0f Deputy of Detention: need Condition like Bishop of Binding 2019-02-08 08:17:01 +00:00
Michael Kamensky
189514f37c Merge branch 'master' into 'master'
Update amplifire.txt

See merge request core-developers/forge!1374
2019-02-08 03:37:12 +00:00
Tim Scott
ee8a45b012 Add test for model being null. This will occur upon deleting a deck. Addresses NPE encountered when a deck is deleted. 2019-02-07 19:05:13 -06:00
T.J. Tillman
983eaaff3c Update amplifire.txt 2019-02-07 20:56:54 +00:00
Michael Kamensky
9d79959b87 Merge branch 'master' into 'master'
Fix Arrester's Zeal

See merge request core-developers/forge!1372
2019-02-07 15:12:04 +00:00
T.J. Tillman
a3d75035b9 Update arresters_zeal.txt 2019-02-07 15:08:26 +00:00
Michael Kamensky
941adbbc68 Merge branch 'patch-3' into 'master'
Knight of the last Breath: fix Sac Description

See merge request core-developers/forge!1373
2019-02-07 09:10:10 +00:00
Hans Mackowiak
67167c29fc Knight of the last Breath: fix Sac Description 2019-02-07 08:41:02 +00:00
T.J. Tillman
d763e3a1db Update arresters_zeal.txt 2019-02-07 06:42:14 +00:00
T.J. Tillman
ebddcb3210 Update spear_spewer.txt 2019-02-07 06:40:11 +00:00
T.J. Tillman
526104104c Merge branch 'forPushing' into 'master'
For pushing

See merge request tjtillmancoag/forge!5
2019-02-07 06:35:27 +00:00
T.J. Tillman
ebd5e7b419 Merge branch 'SpearSpewerFix' into 'master'
Spear spewer fix et all

See merge request tjtillmancoag/forge!4
2019-02-07 06:14:18 +00:00
Michael Kamensky
d491972995 Merge branch 'master' into 'master'
Translating hard-coded text to res en-US.properties

See merge request core-developers/forge!1283
2019-02-07 05:54:47 +00:00
Michael Kamensky
60c68b10d9 Merge branch 'master' into 'master'
Guardian Project fix by friarsol: patch and integrate

See merge request core-developers/forge!1371
2019-02-07 05:05:44 +00:00
Agetian
471fcaa938 - Revert an accidental change in CardProperty. 2019-02-07 08:05:06 +03:00
Agetian
7111c3f3aa Merge branch 'fix-guardian-project' of https://git.cardforge.org/friarsol/forge into agetian-master 2019-02-07 08:04:27 +03:00
Michael Kamensky
384b5c2c5b Merge branch 'SpearSpewerFix' into 'master'
Several RNA fixes

See merge request core-developers/forge!1369
2019-02-07 04:20:13 +00:00
Sol
84a2fed516 Update guardian_project.txt 2019-02-07 03:42:23 +00:00
Chris H
a1c137a86b Fix Guardian Project 2019-02-06 22:04:31 -05:00
T.J. Tillman
87359257fd Update end_raze_forerunners.txt 2019-02-07 02:06:54 +00:00
T.J. Tillman
4e8c18b232 Update angel_of_grace.txt 2019-02-07 01:42:51 +00:00
T.J. Tillman
95f46dfde3 Update smothering_tithe.txt 2019-02-07 01:01:59 +00:00
T.J. Tillman
20c978a64f Revert "Update spear_spewer.txt"
This reverts commit c7d8a112d0
2019-02-06 21:39:56 +00:00
T.J. Tillman
bb57cb1e1b Update kaya_orzhov_usurper.txt 2019-02-06 20:28:43 +00:00
T.J. Tillman
29b0c6eb69 Update kaya_orzhov_usurper.txt 2019-02-06 20:17:25 +00:00
T.J. Tillman
76b929b814 Revert "Update arresters_zeal.txt"
This reverts commit fbde2483e4
2019-02-06 18:47:19 +00:00
T.J. Tillman
fbde2483e4 Update arresters_zeal.txt 2019-02-06 18:41:40 +00:00
T.J. Tillman
55ccd88790 Update descriptions on swirling_torrent.txt 2019-02-06 18:38:32 +00:00
T.J. Tillman
5b9e121674 Update Contamination.dck 2019-02-06 18:29:37 +00:00
T.J. Tillman
d86e04222d Revert "Update Contamination.dck"
This reverts commit 5b10cb0e26
2019-02-06 18:29:08 +00:00
T.J. Tillman
5b10cb0e26 Update Contamination.dck 2019-02-06 18:28:41 +00:00
T.J. Tillman
5d99a0e918 Update pestilent_spirit.txt 2019-02-06 18:21:31 +00:00
T.J. Tillman
8385e80fd3 Revert guardian_project.txt for this branch 2019-02-06 18:00:09 +00:00
T.J. Tillman
c7d8a112d0 Update spear_spewer.txt 2019-02-06 17:56:56 +00:00
Sol
6a715bdab7 Merge branch 'patch-1' into 'master'
Update imperiosaur: fix P/T

See merge request core-developers/forge!1367
2019-02-06 14:22:08 +00:00
Hans Mackowiak
2284964c88 Update imperiosaur: fix P7T 2019-02-06 12:20:37 +00:00
Michael Kamensky
bd83fc675c Merge branch 'lumberingFix' into 'master'
Lumbering Battlement fix using Hidden

See merge request core-developers/forge!1366
2019-02-06 07:07:06 +00:00
Hanmac
7ea81fce8b cards: add missing empty Oracle 2019-02-06 07:28:13 +01:00
Hanmac
519b05fcc1 Lumbering Battlement: use Hidden for ChangeType 2019-02-06 07:25:02 +01:00
Michael Kamensky
8ab3a00a3a Merge branch 'addendumFix' into 'master'
Addendum fix

See merge request core-developers/forge!1362
2019-02-06 05:08:17 +00:00
Hans Mackowiak
544327330d Addendum fix 2019-02-06 05:08:17 +00:00
T.J. Tillman
c3e895936e WIP - Guardian Project
- Works for Nth copy on the Battlefield
- Does NOT work for any copies in the graveyard
- Does not work for flickering the 1st copy on the battlefield (incorrectly draws), need a way to count all the cards on the BF except the triggered card
2019-02-05 22:11:47 +00:00
tehdiplomat
d223089c05 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-02-05 16:20:07 -05:00
Michael Kamensky
f5725766c0 Merge branch 'master' into 'master'
Added puzzle PS_RNA2.

See merge request core-developers/forge!1364
2019-02-05 19:59:28 +00:00
Agetian
585f0299b2 - Revert an accidental debug change commit. 2019-02-05 22:58:39 +03:00
Agetian
3a8c07d9c1 - Added puzzle PS_RNA2. 2019-02-05 22:57:00 +03:00
Agetian
30109aaeba - Added puzzle PS_RNA2. 2019-02-05 22:49:56 +03:00
Michael Kamensky
c02f4cf081 Merge branch 'master' into 'master'
Update dovin_grand_arbiter.txt

See merge request core-developers/forge!1363
2019-02-05 19:26:20 +00:00
T.J. Tillman
2c0ec81812 Update dovin_grand_arbiter.txt 2019-02-05 17:47:34 +00:00
Michael Kamensky
2d8286adf1 Merge branch 'cst' into 'master'
Add "Coldsnap Theme Decks" edition.

See merge request core-developers/forge!1314
2019-02-05 04:33:41 +00:00
Michael Kamensky
95cb17849e Merge branch 'master' into 'master'
"ANA" / Arena New Experience cards added, other card script updates

See merge request core-developers/forge!1299
2019-02-05 04:33:30 +00:00
Michael Kamensky
e42608f856 Merge branch 'master' into 'master'
Update unbreakable_formation.txt (Addendum)

See merge request core-developers/forge!1361
2019-02-05 04:31:52 +00:00
Michael Kamensky
cdb47e0e10 Merge branch 'optimize' into 'master'
optimize redisplay of floating card areas

See merge request core-developers/forge!1337
2019-02-04 20:34:45 +00:00
Michael Kamensky
b008a75043 Merge branch 'bugfix' into 'master'
fix crash when hiding temporarily shown zones

See merge request core-developers/forge!1360
2019-02-04 20:32:39 +00:00
T.J. Tillman
81d3ab86f9 Update unbreakable_formation.txt 2019-02-04 20:23:03 +00:00
Peter F. Patel-Schneider
efe71e8384 fix crash when hiding temporarily shown zones 2019-02-04 14:24:34 -05:00
Sol
cb494280a9 Merge branch 'master' into 'master'
Update kayas_wrath.txt

See merge request core-developers/forge!1359
2019-02-04 13:35:15 +00:00
T.J. Tillman
ba587ea702 Update kayas_wrath.txt 2019-02-04 06:14:39 +00:00
Michael Kamensky
4b00795f3d Merge branch 'transfigureKeyword' into 'master'
CardFactoryUtil: Transfigure as Keyword

See merge request core-developers/forge!1358
2019-02-04 03:36:11 +00:00
Hanmac
faca59beb0 CardFactoryUtil: Transfigure as Keyword 2019-02-03 18:18:06 +01:00
Michael Kamensky
41593d0060 Merge branch 'meldFix' into 'master'
MeldEffect: some fixes

See merge request core-developers/forge!1356
2019-02-03 16:33:07 +00:00
Michael Kamensky
ce865da924 Merge branch 'master' into 'master'
Added puzzle PS_RNA1. Enabled RNA in Planar Conquest.

See merge request core-developers/forge!1357
2019-02-03 16:25:37 +00:00
Agetian
1300194e3a - Added puzzle PS_RNA1.
- Enabled RNA for the Ravnica plane in Planar Conquest (no event updates yet).
2019-02-03 19:24:50 +03:00
Hanmac
903c708e1f MeldEffect: some fixes 2019-02-03 16:37:49 +01:00
Michael Kamensky
b5d97dc256 Merge branch 'surgeFix' into 'master'
SpellAbility: fixed Surge for Crush of Tentacles

See merge request core-developers/forge!1355
2019-02-03 14:56:54 +00:00
Peter F. Patel-Schneider
33c398fcb6 add newline to separate functions 2019-02-03 08:51:32 -05:00
Michael Kamensky
3fc37c735e Merge branch 'master' into 'master'
RNA fixes (Awaken the Erstwhile and Skarrgan Hellkite)

Closes #824

See merge request core-developers/forge!1354
2019-02-03 10:13:16 +00:00
Hanmac
1b44c953fc SpellAbility: fixed Surge for Crush of Tentacles 2019-02-03 08:39:30 +01:00
T.J. Tillman
816d136350 Update awaken_the_erstwhile.txt 2019-02-03 05:47:52 +00:00
T.J. Tillman
cd1bfc6b08 Update awaken_the_erstwhile.txt 2019-02-03 05:46:09 +00:00
T.J. Tillman
e72879d173 Update skarrgan_hellkite.txt 2019-02-03 05:34:42 +00:00
Michael Kamensky
d18ef04536 Merge branch 'master' into 'master'
Fix formatting in Jhoira fix and integrate it

See merge request core-developers/forge!1353
2019-02-03 05:21:01 +00:00
Agetian
426c0ca4c8 - Formatting fix. 2019-02-03 08:20:13 +03:00
Agetian
7b521919fb Merge branch 'fix_jhoira_avatar' of https://git.cardforge.org/friarsol/forge into agetian-master 2019-02-03 08:18:28 +03:00
Michael Kamensky
5b547d509b Merge branch 'master' into 'master'
RNA updates

See merge request core-developers/forge!1351
2019-02-03 05:18:05 +00:00
Chris H
98830dde13 Fix Jhoira Avatar crashing since it copies cards into no zone 2019-02-02 23:23:17 -05:00
Sol
1a5fa823e5 Merge branch 'patch-3' into 'master'
Fix: c_a_treasure_sac

Closes #825

See merge request core-developers/forge!1350
2019-02-03 04:22:50 +00:00
T.J. Tillman
c769aa07dd Update consecrate_consume.txt 2019-02-03 03:06:24 +00:00
T.J. Tillman
10745eb9c7 Update consecrate_consume.txt 2019-02-03 02:34:59 +00:00
T.J. Tillman
18934c7f9f Update revival_revenge.txt 2019-02-03 02:25:36 +00:00
T.J. Tillman
c54682850b Update theater_of_horrors.txt 2019-02-03 00:33:59 +00:00
T.J. Tillman
f241a62d95 Update clear_the_stage.txt 2019-02-03 00:15:59 +00:00
Peter F. Patel-Schneider
31bca6c614 try to do fewer layout calculations when moving cards 2019-02-02 16:46:23 -05:00
Hans Mackowiak
567aab0c43 Fix: c_a_treasure_sac 2019-02-02 21:04:01 +00:00
Michael Kamensky
ef1f1c33cf Merge branch 'giantbaiting-patch' into 'master'
Bug fix: Giantbaiting

See merge request core-developers/forge!1349
2019-02-02 16:42:53 +00:00
Rob Schnautz
a18d98c231 Token script had typo in name. 2019-02-02 16:39:51 +00:00
Michael Kamensky
66b97d5d8e Merge branch 'fix-sarkhan-dragonsoul' into 'master'
Fix Sarkhan, Dragonsoul (ultimate ability) and Kaya, Orzhov Usurper (costs).

Closes #823

See merge request core-developers/forge!1347
2019-02-02 13:24:28 +00:00
Michael Kamensky
3c83b62d98 Merge branch 'landwalkFix' into 'master'
CombatUtil: fix Basic Landwalk abilities

See merge request core-developers/forge!1348
2019-02-02 13:24:17 +00:00
Hanmac
0569a0b36d CombatUtil: fix Basic Landwalk abilities 2019-02-02 14:07:48 +01:00
Agetian
a8abbadec4 - Fix Kaya, Orzhov Usurper ability costs. 2019-02-02 13:58:13 +03:00
Agetian
6235b42c31 - Limit the search to Dragon creature cards. 2019-02-02 13:54:23 +03:00
Agetian
1d41f1b330 - Fix Sarkhan, Dragonsoul. 2019-02-02 13:51:18 +03:00
Michael Kamensky
ad8778d397 Merge branch 'master' into 'master'
Preparing Forge for Android publish 1.6.20.001 [incremental].

See merge request core-developers/forge!1346
2019-02-02 05:56:08 +00:00
Agetian
e38bd51193 - Preparing Forge for Android publish 1.6.20.001 [incremental]. 2019-02-02 08:49:31 +03:00
KrazyTheFox
92ba90bc9e Add Sting deck by timmermac 2019-02-01 23:22:39 -05:00
Blacksmith
e5584ea065 Clear out release files in preparation for next release 2019-02-02 02:05:37 +00:00
Blacksmith
17dedb4bf4 [maven-release-plugin] prepare for next development iteration 2019-02-02 02:00:32 +00:00
Blacksmith
eb3fb9e1ce [maven-release-plugin] prepare release forge-1.6.20 2019-02-02 02:00:28 +00:00
Blacksmith
b27f49c4ff Update README.txt for release 2019-02-02 01:58:00 +00:00
KrazyTheFox
8d4854bd5d Saving progress 2019-02-01 20:39:57 -05:00
Sol
1c33e3b039 Merge branch 'master' into 'master'
Update rakdos_the_showstopper.txt using DestroyAll

See merge request core-developers/forge!1344
2019-02-02 01:35:24 +00:00
T.J. Tillman
eda0a49097 Update token image parameter for Domri, Chaos Bringer 2019-02-02 00:14:16 +00:00
T.J. Tillman
ea8c6630e0 Update cry_of_the_carnarium.txt 2019-02-01 23:31:09 +00:00
T.J. Tillman
0d4310382e Update rakdos_the_showstopper.txt using DestroyAll 2019-02-01 20:10:21 +00:00
Sol
4ae1547384 Merge branch 'patch-3' into 'master'
Update carnival_carnage.txt

See merge request core-developers/forge!1343
2019-02-01 19:11:38 +00:00
Sol
4192195579 Update carnival_carnage.txt 2019-02-01 19:11:15 +00:00
Sol
0d11e28c11 Merge branch 'master' into 'master'
Update Captive Audience to Functional plus a few others

See merge request core-developers/forge!1342
2019-02-01 19:10:41 +00:00
T.J. Tillman
9cf78d0eb4 Update deputy_of_detention.txt 2019-02-01 18:18:04 +00:00
T.J. Tillman
7843004c40 Fixed Ferocious condition 2019-02-01 18:12:25 +00:00
T.J. Tillman
4c7c2ee1b5 User reported Locust God's return trigger wasn't working on Resolution, but that it does work for Scarab God. Changed one word in Locust God's trigger to match Scarab God's 2019-02-01 17:20:23 +00:00
T.J. Tillman
05538bfba2 Update captive_audience.txt 2019-02-01 17:10:37 +00:00
Michael Kamensky
6fe6d6ecfd Merge branch 'master' into 'master'
A few more RNA card script updates

See merge request core-developers/forge!1339
2019-02-01 16:56:41 +00:00
Michael Kamensky
34a8184cc7 Merge branch 'patch-3' into 'master'
Light up the stage: Fix script with Commune with Lava

See merge request core-developers/forge!1341
2019-02-01 16:55:39 +00:00
Hans Mackowiak
5d9f86bf20 Light up the stage: Fix script with Commune with Lava 2019-02-01 16:34:43 +00:00
Michael Kamensky
661555551d Merge branch 'rnabuild' into 'master'
RNA LDA deck generation data

See merge request core-developers/forge!1340
2019-02-01 14:35:40 +00:00
austinio7116
02e118b2e2 RNA deckgen data
(cherry picked from commit eb7a6da)

(cherry picked from commit 32e02e7)
2019-02-01 08:02:02 +00:00
maustin
b59fbe43f0 Merge branch 'coremaster' into rnabuild 2019-02-01 08:01:34 +00:00
T.J. Tillman
ac51d4170f Update rumbling_ruin.txt 2019-02-01 07:37:01 +00:00
Sol
dc91df0e19 Merge branch 'patch-2' into 'master'
Update plaza_of_harmony.txt

See merge request core-developers/forge!1338
2019-02-01 04:20:07 +00:00
Sol
5bce464268 Update plaza_of_harmony.txt 2019-02-01 04:19:56 +00:00
T.J. Tillman
60d5afe9a2 Update eyes_everywhere.txt 2019-02-01 04:02:15 +00:00
T.J. Tillman
ef6e09d4c4 Update fireblade_artist.txt 2019-02-01 03:12:45 +00:00
tehdiplomat
e330cabc64 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-01-31 20:56:32 -05:00
Sol
76324bc6db Merge branch 'master' into 'master'
Fixed Rakdos, the Showstopper

See merge request core-developers/forge!1336
2019-02-01 01:08:25 +00:00
Peter F. Patel-Schneider
30607291fc optimize redisplay of floating card areas 2019-01-31 19:53:05 -05:00
T.J. Tillman
bee8348b67 Update cosmotronic_wave.txt to reflect ruling that this is a continuous effect. 2019-01-31 22:12:14 +00:00
T.J. Tillman
51fb900eb5 Revert "Update rumbling_ruin.txt"
This reverts commit 8b902671d6
2019-01-31 22:06:31 +00:00
T.J. Tillman
8b902671d6 Update rumbling_ruin.txt 2019-01-31 22:02:53 +00:00
T.J. Tillman
7dbbf0a554 Update rakdos_the_showstopper.txt 2019-01-31 19:22:12 +00:00
T.J. Tillman
e75b15b7cb Update rakdos_the_showstopper.txt 2019-01-31 19:20:09 +00:00
tehdiplomat
b2063433c1 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-01-31 12:17:51 -05:00
Michael Kamensky
43cca9b635 Merge branch 'master' into 'master'
Check for alternative additional costs in the AI routines.

Closes #813 and #820

See merge request core-developers/forge!1334
2019-01-31 13:43:48 +00:00
Agetian
b29f390b62 - Fix the AI activating player setup for additional costs.
- Fix Thrilling Encore.
2019-01-31 16:39:49 +03:00
Michael Kamensky
f83a97bedd Merge branch '819-spectra-ward-broken' into 'master'
Resolve "Spectra Ward broken"

Closes #819

See merge request core-developers/forge!1335
2019-01-31 13:24:19 +00:00
Agetian
c58690dd96 Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master 2019-01-31 09:34:27 +03:00
Hans Mackowiak
3402cddd1d GameAction: fix Spectral Ward #819 2019-01-31 06:25:06 +00:00
Agetian
3956a5b558 - Check for alternative additional costs in the AI routines (fixes AI not paying those costs for Final Payment and other cards). 2019-01-31 08:45:53 +03:00
Sol
72b60c91f5 Merge branch 'master' into 'master'
Fix Deputy of Detention

See merge request core-developers/forge!1333
2019-01-31 04:28:00 +00:00
T.J. Tillman
d063e98e33 Update deputy_of_detention.txt 2019-01-31 04:11:09 +00:00
Sol
8743cba5d7 Merge branch 'rna-rankings' into 'master'
Add RNA rankings

See merge request core-developers/forge!1332
2019-01-31 01:25:37 +00:00
Chris H
1d2bbe4923 Add RNA rankings 2019-01-30 20:24:30 -05:00
Michael Kamensky
58311e5265 Merge branch 'fix-gurzigost' into 'master'
Fix Gurzigost.

Closes #817

See merge request core-developers/forge!1331
2019-01-30 16:04:16 +00:00
Agetian
8c020c7c8c - Generic check. 2019-01-30 18:54:15 +03:00
Agetian
ba25696ea7 - Fix Gurzigost. 2019-01-30 18:30:07 +03:00
Michael Kamensky
68359097df Merge branch 'patch-2' into 'master'
CardState: this catch the Exception in case it crash in Keyword.initialize

See merge request core-developers/forge!1330
2019-01-30 15:17:47 +00:00
Hans Mackowiak
4b882ad215 CardState: this catch the Exception in case it crash in Keyword.initialize 2019-01-30 10:22:43 +00:00
Michael Kamensky
ee6806b36a Merge branch 'patch-1' into 'master'
Send SpellAbility into getDefinedPlayers of greatestPower

See merge request core-developers/forge!1329
2019-01-30 04:48:01 +00:00
Sol
343723e529 Send SpellAbility into getDefinedPlayers of greatestPower 2019-01-30 01:34:05 +00:00
Sol
15bf426386 Merge branch 'master' into 'master'
Couple RNA fixes

See merge request core-developers/forge!1328
2019-01-30 01:19:19 +00:00
T.J. Tillman
77184dc930 Update consecrate_consume.txt 2019-01-30 00:13:01 +00:00
T.J. Tillman
5f11897d11 Update consecrate_consume.txt 2019-01-29 23:43:22 +00:00
T.J. Tillman
40764691fd Update code_of_constraint.txt - Doesn't untap only true for Addendum case 2019-01-29 23:38:51 +00:00
T.J. Tillman
210cc6164d Update consecrate_consume.txt 2019-01-29 22:45:38 +00:00
T.J. Tillman
11f597bb23 Fix Consume so that target player will sacrifice its greatest power creature. Not very clean, but used a workaround mimicking Crackling Doom to get it to work. 2019-01-29 22:43:12 +00:00
T.J. Tillman
015623a122 Fix Dovin, Grand Arbiter's +1 ability 2019-01-29 19:57:56 +00:00
T.J. Tillman
b04e16d1ca Update mirror_march.txt 2019-01-29 17:50:02 +00:00
Michael Kamensky
e3f24dccd4 Merge branch 'master' into 'master'
Added RNA achievements (by Marek14).

See merge request core-developers/forge!1327
2019-01-29 14:32:42 +00:00
Agetian
2e0812dd6a - Added RNA achievements (by Marek14). 2019-01-29 17:31:50 +03:00
T.J. Tillman
9deeec7b14 Merge branch 'fixDeputyOfDetention' into 'master'
Fix Deputy of Detention

See merge request tjtillmancoag/forge!3
2019-01-29 08:57:28 +00:00
T.J. Tillman
b58b2b93e2 Fix Deputy of Detention (now exiles other cards of the same name, and also now returns those cards to the BF when it leaves the BF) 2019-01-29 07:37:06 +00:00
Michael Kamensky
2697f5f404 Merge branch 'master' into 'master'
Fixed Sentinel's Mark Lifelink trigger

See merge request core-developers/forge!1325
2019-01-29 05:46:50 +00:00
T.J. Tillman
78db487e8d Merge branch 'sentinelsMarkTrigger' into 'master'
Fix lifelink trigger on Sentinel's Mark

See merge request tjtillmancoag/forge!2
2019-01-29 05:32:53 +00:00
T.J. Tillman
1ecd1a8340 Fix lifelink trigger on Sentinel's Mark 2019-01-29 05:31:58 +00:00
Michael Kamensky
c7539de77d Merge branch 'master' into 'master'
RNA Planeswalker Deck Precons

See merge request core-developers/forge!1324
2019-01-29 05:12:28 +00:00
Agetian
b0ee2cedff - Added RNA planeswalker decks to quest mode precons. 2019-01-29 08:11:50 +03:00
Sol
7a0453390a Merge branch 'migrate-upcoming' into 'master'
Migrate upcoming

See merge request core-developers/forge!1323
2019-01-29 04:13:50 +00:00
Chris H
1b47a2df87 Migrate RNA to alpha folders 2019-01-28 22:58:33 -05:00
Chris H
fd54606277 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-01-28 22:44:44 -05:00
Chris H
25b02141bd Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-01-28 22:43:07 -05:00
Sol
bcc4158956 Merge branch 'someRNAFixes' into 'master'
Fix Spectacle cost for Blade Juggler

See merge request core-developers/forge!1322
2019-01-29 03:42:37 +00:00
T.J. Tillman
fc5b76fbb4 Fix cast cost of Colossus 2019-01-29 03:41:59 +00:00
T.J. Tillman
38ddca0acd Fix Spectacle cost 2019-01-29 03:37:10 +00:00
Sol
7ed935c43c Merge branch 'someRNAFixes' into 'master'
Fix Sphinx of Foresight opening scry timing

See merge request core-developers/forge!1321
2019-01-29 03:32:52 +00:00
T.J. Tillman
14629715a2 Fix cost of spirit-making ability on Ethereal Absolution 2019-01-29 03:22:44 +00:00
T.J. Tillman
e71ff26b81 Update forge-gui/res/cardsfolder/upcoming/sphinx_of_foresight.txt 2019-01-29 00:35:26 +00:00
Sol
7d6dec773a Merge branch 'someRNAFixes' into 'master'
New Cards: RNA Domri planeswalkers

See merge request core-developers/forge!1320
2019-01-28 23:35:33 +00:00
T.J. Tillman
e52bcc6cdc Update forge-gui/res/cardsfolder/upcoming/dovins_automaton.txt 2019-01-28 22:26:02 +00:00
T.J. Tillman
b3e1d96aff Upload New File 2019-01-28 22:06:12 +00:00
T.J. Tillman
e13880c87e Domri, City Smasher 2019-01-28 20:18:07 +00:00
Chris H
0b14efebb8 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-01-28 15:15:06 -05:00
Sol
18a3bfe0b8 Merge branch 'someRNAFixes' into 'master'
A Few RNA card script fixes

See merge request core-developers/forge!1316
2019-01-28 20:08:56 +00:00
T.J. Tillman
d8da15d8ae Update forge-gui/res/cardsfolder/upcoming/dovin_architect_of_law.txt 2019-01-28 19:58:23 +00:00
T.J. Tillman
5bd9f324e0 Update forge-gui/res/cardsfolder/upcoming/dovins_dismissal.txt 2019-01-28 18:09:35 +00:00
T.J. Tillman
e0b463c6ea Update forge-gui/res/cardsfolder/upcoming/cindervines.txt 2019-01-28 18:03:00 +00:00
Michael Kamensky
01742f06f6 Merge branch 'highlight' into 'master'
Darken non-selectable cards in Desktop GUI

See merge request core-developers/forge!1317
2019-01-28 17:55:36 +00:00
T.J. Tillman
dbe212065d Update forge-gui/res/cardsfolder/r/raid_bombardment.txt 2019-01-28 17:44:21 +00:00
T.J. Tillman
4b8584376d Update forge-gui/res/cardsfolder/upcoming/cavalcade_of_calamity.txt 2019-01-28 17:27:48 +00:00
Michael Kamensky
6cf7d3ae80 Merge branch 'master' into 'master'
Added puzzle PS_RNA0a (RNA Prerelease Puzzle)

See merge request core-developers/forge!1319
2019-01-28 12:22:42 +00:00
Agetian
104aaa1255 - Added puzzle PS_RNA0a (RNA Prerelease Puzzle) 2019-01-28 15:22:06 +03:00
Peter F. Patel-Schneider
1ba1226661 refresh hand floating zone when hand is updated 2019-01-28 05:53:38 -05:00
T.J. Tillman
f8bf8c7f28 Update forge-gui/res/cardsfolder/upcoming/cindervines.txt 2019-01-28 08:41:26 +00:00
T.J. Tillman
763cb3d08e Update forge-gui/res/cardsfolder/upcoming/shimmer_of_possibility.txt 2019-01-28 08:30:23 +00:00
T.J. Tillman
5e94c6c8d3 Update forge-gui/res/cardsfolder/upcoming/forbidding_spirit.txt 2019-01-28 08:27:47 +00:00
T.J. Tillman
b08ce49057 Update forge-gui/res/cardsfolder/upcoming/senate_griffin.txt, forge-gui/res/cardsfolder/upcoming/haazda_officer.txt, forge-gui/res/cardsfolder/upcoming/forbidding_spirit.txt, forge-gui/res/cardsfolder/upcoming/domris_nodorog.txt files 2019-01-28 08:07:43 +00:00
T.J. Tillman
7c98ed75be Update forge-gui/res/cardsfolder/upcoming/domris_nodorog.txt, forge-gui/res/cardsfolder/upcoming/forbidding_spirit.txt, forge-gui/res/cardsfolder/upcoming/haazda_officer.txt, forge-gui/res/cardsfolder/upcoming/senate_griffin.txt files 2019-01-28 08:02:07 +00:00
T.J. Tillman
1c9a515bb3 Update forge-gui/res/cardsfolder/upcoming/domris_nodorog.txt 2019-01-28 07:47:55 +00:00
T.J. Tillman
a462d34249 Update forge-gui/res/cardsfolder/upcoming/charging_war_boar.txt, forge-gui/res/cardsfolder/upcoming/cindervines.txt files 2019-01-28 07:38:32 +00:00
T.J. Tillman
e1ceed159f Update forge-gui/res/cardsfolder/upcoming/elite_arrester.txt, forge-gui/res/cardsfolder/upcoming/clamor_shaman.txt files 2019-01-28 07:08:05 +00:00
T.J. Tillman
89757b9677 Update forge-gui/res/cardsfolder/upcoming/footlight_fiend.txt 2019-01-28 07:04:13 +00:00
T.J. Tillman
01c52667ad Update forge-gui/res/cardsfolder/upcoming/cavalcade_of_calamity.txt, forge-gui/res/cardsfolder/r/raid_bombardment.txt, forge-gui/res/cardsfolder/upcoming/forbidding_spirit.txt, forge-gui/res/cardsfolder/upcoming/get_the_point.txt, forge-gui/res/cardsfolder/upcoming/haazda_officer.txt, forge-gui/res/cardsfolder/upcoming/lawmages_binding.txt, forge-gui/res/cardsfolder/upcoming/rubble_slinger.txt, forge-gui/res/cardsfolder/upcoming/rubblebelt_runner.txt, forge-gui/res/cardsfolder/upcoming/screaming_shield.txt, forge-gui/res/cardsfolder/upcoming/scuttlegator.txt, forge-gui/res/cardsfolder/upcoming/senate_griffin.txt, forge-gui/res/cardsfolder/upcoming/summary_judgment.txt, forge-gui/res/cardsfolder/upcoming/vizkopa_vampire.txt files 2019-01-28 07:01:43 +00:00
Peter F. Patel-Schneider
25003aa74e better way to schedule visual updates to non-selectable cards 2019-01-27 21:09:52 -05:00
T.J. Tillman
766277673d Update simic_locket.txt 2019-01-27 23:45:21 +00:00
T.J. Tillman
a1ad2208c8 Update rix_maadi_reveler.txt 2019-01-27 23:01:06 +00:00
T.J. Tillman
0bfdfb78d3 Update ill_gotten_inheritance.txt 2019-01-27 22:43:34 +00:00
T.J. Tillman
b23425ea9a Update bloodmist_infiltrator.txt 2019-01-27 22:37:18 +00:00
T.J. Tillman
8b7ace6d1c Added TrigChangeZone to bounce it back, and CheckSVar to check if instant cast in Main Phase 2019-01-27 21:45:52 +00:00
T.J. Tillman
1dc8ef5244 Fixed problem with self-exiling after trigger resolution (Hanmac) 2019-01-27 20:34:35 +00:00
T.J. Tillman
625585f005 Fixed Scry 3 2019-01-27 20:15:05 +00:00
T.J. Tillman
acdee9ff0c Update fireblade_artist.txt 2019-01-27 19:43:56 +00:00
T.J. Tillman
54c5d3f344 Fix Fireblade Artist to reflect "when you" sacrifice trigger (similar to Heart-Piercer Manticore) 2019-01-27 19:25:20 +00:00
T.J. Tillman
4fa8fa3993 Update sentinels_mark.txt 2019-01-27 18:04:06 +00:00
T.J. Tillman
3b21b2a610 Fix fireblade_artist dealing damage on sacrifice 2019-01-27 17:58:28 +00:00
T.J. Tillman
07324f63d9 Update spawn_of_mayhem.txt 2019-01-27 17:56:47 +00:00
T.J. Tillman
79c3570bcc Update gutterbones.txt 2019-01-27 17:55:48 +00:00
T.J. Tillman
d9400fe069 Update rix_maadi_reveler.txt 2019-01-27 17:55:04 +00:00
Peter F. Patel-Schneider
70f0f1108b darken non-selectable cards during paintChildren 2019-01-27 08:22:50 -05:00
Peter F. Patel-Schneider
62316abf38 darken non-selectable cards when selecting (needs work) 2019-01-27 07:16:54 -05:00
Peter F. Patel-Schneider
e2ddcdda4d stronger highlighting for selectable cards 2019-01-27 07:16:54 -05:00
T.J. Tillman
e604d9d0cb Update consecrate_consume.txt to add Cleanup Remembered 2019-01-27 05:18:49 +00:00
tjtillmancoag
ed218b6e4c Fixed Amplifire (properly changes Power & Toughness),
Consume (of Consecrate // Consume, works now),
Final Payment (allows sacrifice of enchantment/creature as payment),
Incongruity (fixed A:AB$ to A:SP$),
Sentinel's Mark (gives Lifelink on Main phase trigger)
Silhana Wayfinder (triggers on ETB)
Smelt Ward Ignus (loses control of creature at EoT)
Undercity's Embrace (no life amount in gainlife ability was causing crash on cast)
2019-01-26 19:39:04 -08:00
Michael Kamensky
866c959036 Merge branch 'spellControllerFix' into 'master'
Spell: fix casting opponent spells

Closes #811

See merge request core-developers/forge!1315
2019-01-26 14:31:12 +00:00
Hanmac
b2616860c0 Spell: fix casting opponent spells 2019-01-26 13:28:55 +01:00
Hans Mackowiak
0ceb882ab0 Merge branch 'charmRework' into 'master'
Charm rework

Closes #806

See merge request core-developers/forge!1312
2019-01-26 09:24:25 +00:00
Hans Mackowiak
560dc572c2 Charm rework 2019-01-26 09:24:25 +00:00
Rob Schnautz
e891e0d31d Add "Coldsnap Theme Decks" edition. 2019-01-26 03:05:49 +00:00
Tim Scott
3c1b106837 Add balance of ANA cards as found at https://scryfall.com/sets/ana 2019-01-25 19:37:19 -06:00
Tim Scott
eefa4e46d1 Correct Druid of Horns so SpellAbility now generates a 3/3 beast. An aura targeting the Druid is supposed to do this. 2019-01-25 17:11:27 -06:00
tehdiplomat
581ef20405 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-01-25 13:01:27 -05:00
Michael Kamensky
27bae4a72b Merge branch 'fix-rna-sealed' into 'master'
Fix up the RNA printsheets a little bit (wrong card names, wrong set IDs)

See merge request core-developers/forge!1313
2019-01-25 15:02:16 +00:00
Agetian
6686937742 - Fix up the RNA printsheets a little bit (wrong card names, wrong set IDs) 2019-01-25 18:01:24 +03:00
Michael Kamensky
d6a466df11 Merge branch 'fix-rna-sealed' into 'master'
Fix AI sealed booster generation for Ravnica Allegiance

Closes #801

See merge request core-developers/forge!1311
2019-01-25 14:27:08 +00:00
Agetian
7eedd04aa1 - Add basic lands definitions to Ravnica Allegiance.
- Revert blocks.txt to use RNA for the land set.
2019-01-25 17:21:21 +03:00
Agetian
e3c9a40a17 - Use M19 lands as the default basic lands for GRN and RNA sealed/draft, since neither set has basic lands of its own in actual booster packs. 2019-01-25 17:17:40 +03:00
Agetian
76ebfa9460 - Fix AI sealed booster generation for Ravnica Allegiance (use GRN basic lands). 2019-01-25 17:04:09 +03:00
tehdiplomat
6cdae2e5ff Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-01-25 08:53:40 -05:00
Michael Kamensky
e0fd812590 Merge branch 'patch-teysa' into 'master'
TriggerHandler: Only real die can look back

Closes #797

See merge request core-developers/forge!1298
2019-01-25 04:55:39 +00:00
Michael Kamensky
2c2104dfe6 Merge branch 'deckimport' into 'master'
DeckRecognizer: Recognize split card names with single slash

See merge request core-developers/forge!1304
2019-01-25 04:53:36 +00:00
Evan Murawski
4661a19982 DeckRecognizer: Recognize split card names with single slash 2019-01-25 04:53:36 +00:00
Hans Mackowiak
9778f9e3a0 Teysa and Gitrog fixes 2019-01-24 21:26:17 +01:00
Hanmac
b6019c6308 GameSimulatorTest: second test for Teysa 2019-01-24 07:45:36 +01:00
Hans Mackowiak
7006c548a5 GameSimulatorTest: Teysa Karlov and Xathrid Necromancer 2019-01-24 07:45:36 +01:00
Hans Mackowiak
2eae26cefb TriggerHandler: Only real die can look back
Rework Panharmonicon
2019-01-24 07:43:14 +01:00
Michael Kamensky
ccd1117762 Merge branch 'riotDouble' into 'master'
Riot: Make Riot Enchantment look into the future

See merge request core-developers/forge!1303
2019-01-24 06:42:11 +00:00
Hanmac
1917871b98 GameSimulatorTest: assert check for DoubleRiot 2019-01-24 07:28:14 +01:00
Hanmac
96e6111400 Riot: Make Riot Enchantment look into the future 2019-01-24 07:27:22 +01:00
Michael Kamensky
f8d360f25f Merge branch 'master' into 'master'
Restore AI SVars for a couple cards.

See merge request core-developers/forge!1310
2019-01-24 05:38:38 +00:00
Agetian
5901c6b66b - Restore AI SVars. 2019-01-24 08:38:11 +03:00
Michael Kamensky
b9782f9790 Merge branch 'master' into 'master'
Integrate Schnautzr's token updates and fix merge conflicts.

See merge request core-developers/forge!1309
2019-01-24 05:37:17 +00:00
Agetian
74c69f0d37 - Integrate Schnautzr's token updates and fix merge conflicts. 2019-01-24 08:36:35 +03:00
Michael Kamensky
5a81cdc40d Merge branch 'master' into 'master'
Added more AI SVars for RNA, fixed Dross Scorpion trigger description. Labeled some buggy RNA scripts.

See merge request core-developers/forge!1308
2019-01-24 05:28:34 +00:00
Agetian
7ebe97f802 - Added TODO labels. 2019-01-24 08:27:54 +03:00
Agetian
956ab8e96e - Added more AI SVars for RNA.
- Added some comments for the needed script fixes.
- Fixed Dross Scorpion trigger description.
2019-01-24 08:08:52 +03:00
Rob Schnautz
b821ec7af5 Add token scripts and download URLs. 2019-01-24 04:57:54 +00:00
Chris H
6a92f68602 Merge branch 'master' of https://git.cardforge.org/core-developers/forge 2019-01-23 22:23:47 -05:00
Sol
d0c5bcf053 Merge branch 'patch-2' into 'master'
Update Ravnica Allegiance.txt

See merge request core-developers/forge!1306
2019-01-24 02:55:00 +00:00
Sol
4069b603c2 Update Ravnica Allegiance.txt 2019-01-24 02:54:49 +00:00
Sol
0813f9bc66 Merge branch 'more-rna' into 'master'
More rna

See merge request core-developers/forge!1305
2019-01-24 02:52:52 +00:00
Sol
0687fc2a31 More rna 2019-01-24 02:52:52 +00:00
Chris H
3f52a8c4f5 Merge remote-tracking branch 'origin/master' 2019-01-23 21:10:33 -05:00
Sol
82e276c661 Merge branch 'more-rna' into 'master'
RNA Templates

See merge request core-developers/forge!1291
2019-01-24 01:48:54 +00:00
Michael Kamensky
195fe58dcb Merge branch 'master' into 'master'
fix bug in min number of cards to select in digs

See merge request core-developers/forge!1302
2019-01-23 09:38:00 +00:00
Peter F. Patel-Schneider
afd1070850 fix bug in min number of cards to select in digs 2019-01-23 03:42:45 -05:00
Chris H
8580d585df Fix Mirror March 2019-01-22 21:42:46 -05:00
maustin
090d6ad9fb Merge branch 'more-rna' of https://git.cardforge.org/friarsol/forge into rnabuild 2019-01-22 06:47:12 +00:00
Michael Kamensky
8ed1ad4afc Merge branch 'master' into 'master'
remember old location and size of pop-up card list window

See merge request core-developers/forge!1300
2019-01-22 04:18:38 +00:00
Chris H
bc854f4d42 RNA Blockdata 2019-01-21 22:23:45 -05:00
Chris H
d8585fd925 Kaya 2019-01-21 22:23:15 -05:00
Chris H
5ae5fef43e Fix more splits 2019-01-21 21:54:51 -05:00
Chris H
48a0ac6039 missing split for Depose//Deploy 2019-01-21 21:53:26 -05:00
Chris H
57a3462623 Cleanup weird Light up the Stage 2019-01-21 21:18:22 -05:00
Chris H
929b5fbcfe Fix some more broken cards 2019-01-21 21:08:36 -05:00
Chris H
56fda56807 Fixes 2019-01-21 21:08:35 -05:00
Chris H
872defd992 Fix Bedazzle 2019-01-21 21:08:30 -05:00
Chris H
e78202e432 Incubation // Incongruity 2019-01-21 21:08:28 -05:00
Chris H
657d85530a Repudiate//replicate 2019-01-21 21:08:27 -05:00
Chris H
a6e0f9b472 Warrant/Warden 2019-01-21 21:08:25 -05:00
Chris H
86abb0d1d5 Fixing more broken RNA cards 2019-01-21 21:08:24 -05:00
Chris H
28a5bd109e Move broken to upcoming 2019-01-21 21:08:22 -05:00
Chris H
b357096da2 Depost deploy 2019-01-21 21:08:21 -05:00
Chris H
d1d1df27bc Consecrate Consume 2019-01-21 21:08:20 -05:00
Chris H
d7ebafe883 Collision Colossus 2019-01-21 21:08:19 -05:00
Chris H
f09130eb86 Carnival Carnage 2019-01-21 21:08:18 -05:00
Chris H
50e6a06478 Fix split cards 2019-01-21 21:08:17 -05:00
Chris H
65fc4a4371 RNA Templates 2019-01-21 21:08:16 -05:00
churrufli
84ef513489 New 5 Gauntlet Contest - Starting Magic Core Set 2019 Welcome Decks 2019-01-22 00:35:42 +01:00
Peter F. Patel-Schneider
67ca319674 remove debugging print in manipulateCardList 2019-01-21 18:10:30 -05:00
Peter F. Patel-Schneider
1f4142e368 remember old location and size of pop-up card list window 2019-01-21 18:05:19 -05:00
Tim Scott
29fd940a3d Add Arena NPE decks.
Correct typo in README.md.
2019-01-21 15:21:35 -06:00
Tim Scott
480792966b Add Arena New Player Experience cards.
Update README with minor card scripting info.
2019-01-21 12:08:01 -06:00
Michael Kamensky
b8147a2e58 Merge branch 'master' into 'master'
use multi-select for dig

Closes #799

See merge request core-developers/forge!1296
2019-01-21 14:47:08 +00:00
Michael Kamensky
2b8a756932 Merge branch 'schnautzr-master-patch-51486' into 'master'
Collector Number Sort update for RNA

See merge request core-developers/forge!1297
2019-01-21 05:06:20 +00:00
Rob Schnautz
f78bfce802 RNA looks at artifact casting cost only 2019-01-21 03:31:16 +00:00
Peter F. Patel-Schneider
ebcb4e28de Add desktop GUI and Dig changes to CHANGES.txt 2019-01-20 21:23:16 -05:00
Peter F. Patel-Schneider
f0c45cf814 Add visual chooser for two lists and use it for and/or dig 2019-01-20 21:23:16 -05:00
Peter F. Patel-Schneider
aff8d5ce01 Use chooseEntitiesForEffect for dig (except and/or dig) 2019-01-20 21:23:16 -05:00
Peter F. Patel-Schneider
94064a2a13 Fix bug in chooseEntitiesforEffect in Mobile GUI 2019-01-20 21:16:34 -05:00
Sol
6359021370 Merge branch 'master' into 'master'
Fix for Invalid Deck or Proxy File Names

Closes #560

See merge request core-developers/forge!1279
2019-01-21 00:55:35 +00:00
Evan Murawski
6090bc8117 Fix for Invalid Deck or Proxy File Names 2019-01-21 00:55:35 +00:00
churrufli
8bf2bd6ddc Translating hard-coded text to res en-US.properties 2019-01-21 00:43:16 +01:00
Peter F. Patel-Schneider
264744645e Merge branch 'highlighting' 2019-01-20 16:41:50 -05:00
Michael Kamensky
ab1e1d2386 Merge branch 'master' into 'master'
put a cyan border on valid cards when selecting targets and costs (desktop UI only)

See merge request core-developers/forge!1281
2019-01-20 19:27:11 +00:00
Hans Mackowiak
87ad257c46 Merge branch 'damageMapClear' into 'master'
DamageMap: add clear after being triggered

See merge request core-developers/forge!1290
2019-01-20 09:12:04 +00:00
Sol
bc5e7f07ae Merge branch 'gift-pack-edition' into 'master'
M19 Gift Pack edition file

See merge request core-developers/forge!1293
2019-01-20 02:59:39 +00:00
Chris H
f18651be7a M19 Gift Pack edition file 2019-01-19 21:02:24 -05:00
Chris H
9921f58155 M19 Gift Pack edition file 2019-01-19 20:58:35 -05:00
Sol
3ade967f85 Merge branch 'patch-1' into 'master'
Update skitter_eel.txt

See merge request core-developers/forge!1292
2019-01-20 01:44:01 +00:00
Sol
243c90ced9 Update skitter_eel.txt 2019-01-20 01:43:46 +00:00
Hanmac
c1c421fff0 DamageMap: add clear after being triggered 2019-01-19 18:11:55 +01:00
Michael Kamensky
bf6f8048d6 Merge branch 'spellCostReduceTarget' into 'master'
CostAdjustment: use Relative for Amount that depends on the SpellAbility

See merge request core-developers/forge!1289
2019-01-19 12:52:12 +00:00
Hanmac
f26935f37f CostAdjustment: use Relative for Amount that depends on the SpellAbility, only works for Self 2019-01-19 13:27:19 +01:00
Michael Kamensky
f3f7700ba9 Merge branch 'patch-1' into 'master'
Update sharktocrab.txt

See merge request core-developers/forge!1288
2019-01-19 10:41:41 +00:00
Hans Mackowiak
2817c4ef61 Update sharktocrab.txt 2019-01-19 10:28:33 +00:00
Michael Kamensky
166700b573 Merge branch 'master' into 'master'
Added puzzle PS_RNA0 (Ravnica Allegiance Card Preview Puzzle).

See merge request core-developers/forge!1287
2019-01-19 10:14:20 +00:00
Agetian
6edb5ad591 - Added puzzle PS_RNA0 (Ravnica Allegiance Card Preview Puzzle). 2019-01-19 13:13:41 +03:00
Hans Mackowiak
66f2ab4e91 Merge branch 'patch-1' into 'master'
Update sharktocrab

See merge request core-developers/forge!1286
2019-01-19 10:11:43 +00:00
Hans Mackowiak
1f211e6ed5 Update sharktocrab 2019-01-19 10:11:15 +00:00
Michael Kamensky
41093c2f7f Merge branch 'ai-hints-rna' into 'master'
First pass of AI hints for RNA.

See merge request core-developers/forge!1284
2019-01-19 10:02:13 +00:00
Agetian
f4df89dca8 Merge remote-tracking branch 'origin/ai-hints-rna' into ai-hints-rna
# Conflicts:
#	forge-gui/res/cardsfolder/upcoming/ethereal_absolution.txt
#	forge-gui/res/cardsfolder/upcoming/watchful_giant.txt
2019-01-19 10:27:27 +03:00
Agetian
ffabf14192 - AI hints for RNA. 2019-01-19 10:25:43 +03:00
Michael Kamensky
5ce9db0b08 Merge branch 'upcomingFix20190119' into 'master'
Fix for upcoming cards

Closes #798

See merge request core-developers/forge!1285
2019-01-19 07:23:14 +00:00
Hanmac
5165c16233 Fix for upcoming cards 2019-01-19 07:57:08 +01:00
Agetian
1e34fd27fe - AI hints for RNA. 2019-01-19 09:34:58 +03:00
Sol
12b464dbcb Merge branch 'Austin/forge-ravnicaallegiance' into 'master'
Austin/forge ravnicaallegiance

See merge request core-developers/forge!1275
2019-01-19 02:53:56 +00:00
Chris H
76e3f0305b Do Type changes affect Svar keys? That sounds bad 2019-01-18 21:29:24 -05:00
Chris H
bb57003518 Biogenic Ooze -> TokenScripts 2019-01-18 21:26:47 -05:00
Chris H
dfbfcf0a11 Hero of Precinct One -> TokenScripts 2019-01-18 21:24:29 -05:00
Chris H
c4c061d669 Fix Rampage of the Clans 2019-01-18 21:23:05 -05:00
Chris H
e76db8e7ff Fix Gruul Spellbreaker affected 2019-01-18 21:15:23 -05:00
Chris H
9c4fd8b570 Fix Spectacle 2019-01-18 21:02:36 -05:00
Chris H
5d2e7a16d3 Fix Afterlife crashes 2019-01-18 21:02:36 -05:00
Chris H
cf3a28b120 Remove bogus card 2019-01-18 21:02:35 -05:00
austinio7116
10f8616e37 First pass of remaining spoilers, commiting and fixing up those that forgescribe got close 2019-01-18 21:02:32 -05:00
maustin
1bade895e1 Complete Ravnica Allegiance editions file 2019-01-18 21:00:53 -05:00
austinio7116
1940bca932 More straightforward RNA cards 2019-01-18 21:00:46 -05:00
austinio7116
620c27a117 Attempts to fix comments in MR - correct RemAI, fix combine guildname move counters and add X Svar to electrodominance 2019-01-18 21:00:43 -05:00
austinio7116
3071f1f74b First batch of easy forgescribe cleanups with editions file 2019-01-18 21:00:42 -05:00
churrufli
07c2065917 Translating hard-coded text to res en-US.properties 2019-01-18 08:20:02 +01:00
Michael Kamensky
0f44b644c1 Merge branch 'bugfix' into 'master'
fix crash when selecting players

See merge request core-developers/forge!1282
2019-01-18 05:08:51 +00:00
Peter F. Patel-Schneider
427b2973b8 Merge branch 'bugfix' 2019-01-17 16:09:50 -05:00
Peter F. Patel-Schneider
25d1d2bb20 Merge branch 'bugfix' into highlighting
Bring bug fix into highlight code
2019-01-17 16:07:49 -05:00
Peter F. Patel-Schneider
8f2123f183 add fix to crash when selecting targets 2019-01-17 16:02:22 -05:00
Peter F. Patel-Schneider
595fbae34f Fix crash when selecting a player 2019-01-17 15:37:59 -05:00
Peter F. Patel-Schneider
3fd842c1e4 Merge branch 'master' of https://git.cardforge.org/core-developers/forge
Bactrack to upstream master to fix bug
2019-01-17 15:30:09 -05:00
Peter F. Patel-Schneider
11f9520c1e Highlight sa targets in Desktop GUI 2019-01-17 09:35:52 -05:00
Michael Kamensky
40c64a943a Merge branch 'patch-1' into 'master'
FIX Protean Raider

See merge request core-developers/forge!1280
2019-01-17 06:01:13 +00:00
Hans Mackowiak
4f953bf2e1 FIX Protean Raider 2019-01-17 05:42:10 +00:00
Peter F. Patel-Schneider
c7d9646f7f adjust highlighting for new manipulation interface 2019-01-16 13:10:56 -05:00
Peter F. Patel-Schneider
d4b4dc5ba0 Remove debugging print 2019-01-16 12:09:13 -05:00
Peter F. Patel-Schneider
2cd86bfe1e put yellow border on selectable cards 2019-01-16 12:09:13 -05:00
Peter F. Patel-Schneider
2f9fb96d29 use Iterators instead of Lists where possible in manipulateCardList 2019-01-16 12:06:21 -05:00
Michael Kamensky
39204513fb Merge branch 'master' into 'master'
Added puzzle PS_CFB (Possibility Storm: How Much Fireball Can You Channel).

See merge request core-developers/forge!1278
2019-01-16 07:17:02 +00:00
Agetian
ebaa96004f - Added puzzle PS_CFB (How Much Fireball Can You Channel). 2019-01-16 10:16:20 +03:00
Michael Kamensky
d381251472 Merge branch 'master' into 'master'
code cleanup for moveable card displays; add movement by clicking to display-based scry

See merge request core-developers/forge!1276
2019-01-16 07:07:16 +00:00
Michael Kamensky
f00e759a93 Merge branch 'removeCountersAnyNumber' into 'master'
CountersRemoveEffect: add ValidSource and CounterNum$ Any

See merge request core-developers/forge!1277
2019-01-16 07:05:06 +00:00
Hanmac
aab63e2029 CountersRemoveEffect: add ValidSource and CounterNum$ Any 2019-01-16 07:30:24 +01:00
Peter F. Patel-Schneider
3749f2a5de fix bug in computing manipulable card views 2019-01-15 22:10:05 -05:00
Peter F. Patel-Schneider
aa7ad578d5 change manipulateCardList to CardView 2019-01-15 06:24:11 -05:00
Peter F. Patel-Schneider
fb80dece04 ListCardArea augment clicking 2019-01-15 06:24:11 -05:00
Peter F. Patel-Schneider
482ab4b87c Add left-click to top and right-click to bottom for ListCardArea 2019-01-15 06:24:11 -05:00
Peter F. Patel-Schneider
f3f9a915d5 Base FloatingZone on FloatingCardArea 2019-01-15 06:24:11 -05:00
Peter F. Patel-Schneider
52184e24ce Add intermediate FloatingCardArea to hold commonalities between FloatingZone and ListCardArea 2019-01-15 06:24:11 -05:00
Michael Kamensky
b6dbfcee96 Merge branch 'master' into 'master'
fixes for multi-player scry; fix bug when human player scrying entire library

See merge request core-developers/forge!1271
2019-01-15 10:59:30 +00:00
Peter F. Patel-Schneider
e07be68786 Remove unused import in GameAction 2019-01-14 07:40:50 -05:00
Peter Patel-Schneider
9c3ae840b8 Merge branch 'pfps_master' into 'master'
GameAction: add PlayerController:confirmMulliganScry

See merge request pfps/forge!1
2019-01-14 01:43:38 +00:00
Hanmac
20eba0fbd7 GameAction: add PlayerController:confirmMulliganScry 2019-01-13 22:01:22 +01:00
Peter F. Patel-Schneider
735516e6d5 Fix bug in scry when scrying entire library 2019-01-13 10:25:35 -05:00
Peter F. Patel-Schneider
087495f5a0 Force AI to accept mulligan scry (otherwise there is a crash) 2019-01-13 10:24:40 -05:00
Peter F. Patel-Schneider
b94ec24948 Changes to do parts of multi-player scry in correct order 2019-01-13 10:08:29 -05:00
Michael Kamensky
e3257e025d Merge branch 'sunburst' into 'master'
Sunburst: rewrite using ETB counter

See merge request core-developers/forge!1269
2019-01-13 14:10:44 +00:00
Hans Mackowiak
2640a509f9 Sunburst: rewrite using ETB counter 2019-01-13 14:10:44 +00:00
Michael Kamensky
e2eb957af9 Merge branch 'sentry2019Breadcrumbs' into 'master'
Sentry: add more Breadcumbs

See merge request core-developers/forge!1274
2019-01-13 09:44:15 +00:00
Hans Mackowiak
81f07cb8a3 Sentry: add more Breadcumbs 2019-01-13 09:44:15 +00:00
Michael Kamensky
1e3cb2e66c Merge branch 'multiplayer-mulligan' into 'master'
Remove partial paris mulligan

See merge request core-developers/forge!1273
2019-01-13 09:17:00 +00:00
Michael Kamensky
76da5b0cac Merge branch 'token_asterisk_replacement' into 'master'
Replace asterisks with x's for token filenames

See merge request core-developers/forge!1272
2019-01-13 09:15:53 +00:00
Chris H
fdc1c32287 Remove partial paris mulligan 2019-01-12 23:49:09 -05:00
Chris H
3b38547fc9 Replace asterisks with x's for token filenames 2019-01-12 22:55:23 -05:00
Michael Kamensky
53d1716255 Merge branch 'deckAnyNumber' into 'master'
DeckFormat: A deck can have any number of cards named CARDNAME.

See merge request core-developers/forge!1270
2019-01-12 16:13:00 +00:00
Hans Mackowiak
0606a00942 DeckFormat: A deck can have any number of cards named CARDNAME. 2019-01-12 16:13:00 +00:00
swordshine
af3c645521 Merge branch 'adaptReduceCost' into 'master'
Adapt: add ReduceCost for Pteramander

See merge request core-developers/forge!1267
2019-01-12 07:49:50 +00:00
swordshine
e5093c6d2f Merge branch 'patch-2' into 'master'
Fix vicious_rumors.txt not discarding

See merge request core-developers/forge!1268
2019-01-12 07:48:49 +00:00
Sol
59102b0e08 Update vicious_rumors.txt 2019-01-12 01:36:29 +00:00
Hanmac
17cff99c2b Adapt: add ReduceCost for Pteramander 2019-01-11 21:15:27 +01:00
Michael Kamensky
7d0dbff8bc Merge branch 'master' into 'master'
Select cards from any Zone that can be shown and do arrangeForScry by popping up library and moving cards (in desktop GUI); Pass min/max card selections through to GUI

See merge request core-developers/forge!1260
2019-01-11 17:08:50 +00:00
Sol
6974e2de27 Merge branch 'patch-2' into 'master'
CostPayLife: fix unused import

See merge request core-developers/forge!1266
2019-01-11 13:27:37 +00:00
Hans Mackowiak
770ed4524a CostPayLife: fix unused import 2019-01-11 13:24:33 +00:00
Hans Mackowiak
7f4dc85554 Merge branch '793-new-feature-paylife-trigger' into 'master'
New Feature: PayLife Trigger

Closes #793

See merge request core-developers/forge!1265
2019-01-11 13:01:08 +00:00
Hans Mackowiak
005fa3d732 Font of Agonies: use new PayLife trigger 2019-01-11 10:28:27 +00:00
Hans Mackowiak
bbe8d79400 Player: add PayLife Trigger 2019-01-11 10:16:29 +00:00
Hans Mackowiak
edaab9d7de TriggerType: add PayLife 2019-01-11 09:38:02 +00:00
Hans Mackowiak
842c6e681d TriggerPayLife: add new Trigger 2019-01-11 09:33:56 +00:00
Hans Mackowiak
05be8406cc Update TriggerLifeGained: better format 2019-01-11 09:31:53 +00:00
Hans Mackowiak
7aa879fb8e Update CostDraw: fix Description 2019-01-11 09:19:12 +00:00
Hans Mackowiak
1e6a9b8c26 Update CostDamage, fix Description 2019-01-11 09:17:55 +00:00
Hans Mackowiak
b2e9c88f62 CostPayLife: refund never called and doesn't work
negative amount can't be paid in the function
2019-01-11 09:16:50 +00:00
Peter F. Patel-Schneider
3609ff9eff only use new input methods if in desktop GUI; remove debugging prints 2019-01-11 03:48:42 -05:00
Peter F. Patel-Schneider
09fc3ae60c add new GUI interface allowing cards to be moved around in a list and use for arrangeForScry if preference UI_SELECT_FROM_CARD_DISPLAYS is set 2019-01-09 21:13:09 -05:00
Michael Kamensky
948c13dd15 Merge branch 'deckicons' into 'master'
Add icons to duels and challenges in LEB and ARN worlds

See merge request core-developers/forge!1262
2019-01-09 06:04:07 +00:00
Rob Schnautz
b8f7d08fae Remove extraneous line 2019-01-09 05:50:07 +00:00
Rob Schnautz
576b7e2dd3 fix kane 2019-01-09 04:35:52 +00:00
Rob Schnautz
024b2e1a01 Add new quest opponent avatars to the downloader. 2019-01-09 04:23:24 +00:00
Rob Schnautz
daf4b9d974 ARN icons and some flavor text. 2019-01-08 03:25:43 +00:00
Rob Schnautz
36b3431975 rest of the LEB world icons 2019-01-08 02:54:36 +00:00
Rob Schnautz
3ded3926f4 Remove placeholder icons for shiny new icons. 2019-01-08 02:50:58 +00:00
Peter F. Patel-Schneider
69502dd97b Pass min/max through choice code so that user can't choose too many cards 2019-01-06 12:07:19 -05:00
Peter F. Patel-Schneider
234304f9ec Add option to select cards from any Zone that can be shown (implemented in desktop GUI only for now) 2019-01-06 10:47:20 -05:00
Michael Kamensky
193a1cc255 Merge branch 'teysaKarlov' into 'master'
Teysa Karlov: add Dieharmonicon to TriggerHandler

See merge request core-developers/forge!1259
2019-01-06 11:12:38 +00:00
Hanmac
d0b569a07f Teysa Karlov: add Dieharmonicon to TriggerHandler 2019-01-06 11:38:58 +01:00
Michael Kamensky
b17edd31ac Merge branch 'tapAttacker' into 'master'
Taps Trigger now flag for being used as Attacker

See merge request core-developers/forge!1258
2019-01-06 05:34:09 +00:00
Hanmac
891f61701c Taps Trigger now flag for being used as Attacker 2019-01-05 20:52:40 +01:00
Hans Mackowiak
e35f5098b9 Merge branch 'charmNone' into 'master'
CharmEffect: now allows zero choices

See merge request core-developers/forge!1257
2019-01-05 12:01:30 +00:00
Hans Mackowiak
55da7435f8 CharmEffect: now allows zero choices 2019-01-05 12:01:30 +00:00
Michael Kamensky
f0a561b1ee Merge branch 'adaptBio' into 'master'
Adapt changes for Biomancer's Familiar

See merge request core-developers/forge!1256
2019-01-04 19:38:37 +00:00
Hans Mackowiak
bbd48d1033 Adapt changes for Biomancer's Familiar 2019-01-04 19:38:37 +00:00
Hans Mackowiak
847013c6c7 Merge branch 'morphSimFix' into 'master'
fixes for game sim

See merge request core-developers/forge!1255
2019-01-04 09:23:47 +00:00
Hanmac
61aa144631 fixes for game sim 2019-01-03 21:44:18 +01:00
Michael Kamensky
dd87f74eb4 Merge branch 'atEOTSvar' into 'master'
At eot svar

See merge request core-developers/forge!1254
2019-01-03 17:47:16 +00:00
Hanmac
95dbc5333e updated card scripts, remove EndOfTurnLeavePlay, add tokenscripts 2019-01-03 16:02:04 +01:00
Hanmac
0d2114416c extend AtEOT, fix resetTurnActivations 2019-01-03 15:54:53 +01:00
Michael Kamensky
215808b011 Merge branch 'facedown-reveal-fix' into 'master'
Fixed the reveal for face-down cards leaving battlefield/stack not working anymore.

See merge request core-developers/forge!1253
2018-12-30 10:43:37 +00:00
Agetian
1ced8bba7d - Style fix. Added a comment. 2018-12-30 08:26:34 +03:00
Agetian
468ff7fc50 - Fixed the reveal for face-down cards leaving battlefield/stack not working anymore. 2018-12-30 08:19:29 +03:00
Hans Mackowiak
0eed2c67d1 Merge branch 'morphRework' into 'master'
Morph and Manifest are not to facedown anymore

See merge request core-developers/forge!1252
2018-12-29 19:18:35 +00:00
Hanmac
4168ef0b0d Morph and Manifest are not to facedown anymore 2018-12-29 15:45:20 +01:00
Michael Kamensky
bf59d932f4 Merge branch 'master' into 'master'
XMage cube ports update

See merge request core-developers/forge!1251
2018-12-27 18:21:14 +00:00
Agetian
765b0e4625 - XMage cube ports update (2 new cubes). 2018-12-27 21:19:45 +03:00
Michael Kamensky
16058bbea2 Merge branch 'tweak-target-overlay' into 'master'
Tweak target overlay

See merge request core-developers/forge!1248
2018-12-27 18:09:24 +00:00
Michael Kamensky
f47f95cd2e Merge branch 'seanceCopyFix' into 'master'
Seance: use AddTypes and AtEOT instead of extra Clones defined

See merge request core-developers/forge!1249
2018-12-27 18:09:00 +00:00
Hanmac
7f0f62abb6 Seance: use AddTypes and AtEOT instead of extra Clones defined 2018-12-27 17:31:34 +01:00
Hans Mackowiak
22627b57f0 Merge branch 'patch-2' into 'master'
Update growth_spiral.txt

Closes #792

See merge request core-developers/forge!1250
2018-12-27 16:23:02 +00:00
Hans Mackowiak
5a81cace02 Update growth_spiral.txt 2018-12-27 16:21:28 +00:00
Tim Scott
3f172e6872 Merge remote-tracking branch 'remotes/upstream-forge/master' into tweak-target-overlay 2018-12-27 08:08:00 -06:00
Michael Kamensky
52dcdaa47d Merge branch 'master' into 'master'
AI damage chaining: Eliminated a redundant call to canPlay (already tested at this point).

See merge request core-developers/forge!1247
2018-12-27 10:18:41 +00:00
Agetian
d4476cbd2b - Eliminated a redundant call to canPlay (already tested above). 2018-12-27 13:17:49 +03:00
Michael Kamensky
d2c4ab28c2 Merge branch 'master' into 'master'
Fixed the AI misplaying with Chamber Sentry.

Closes #791

See merge request core-developers/forge!1246
2018-12-27 05:23:31 +00:00
Agetian
23d67ab0cf - Fixed the AI misplaying with Chamber Sentry. 2018-12-27 08:21:28 +03:00
Tim Scott
3f55b9f503 Flesh out README. 2018-12-26 10:18:40 -06:00
Tim Scott
e222444809 Update readme with build info. 2018-12-26 09:32:22 -06:00
Tim Scott
83ed648966 Merge remote-tracking branch 'remotes/upstream-forge/master' into tweak-target-overlay 2018-12-26 08:44:28 -06:00
Hans Mackowiak
a5c1d88460 Merge branch 'putLandOptionalFix' into 'master'
Fix 'You may put' 'land card from your hand onto the battlefield' cards

See merge request core-developers/forge!1245
2018-12-26 10:28:58 +00:00
Hanmac
fee83e0db4 Fix 'You may put' 'land card from your hand onto the battlefield' cards 2018-12-26 11:19:23 +01:00
Tim Scott
dadf1b12d3 Add README file. 2018-12-25 10:55:00 -06:00
Tim Scott
5ff8298b62 Fix issue with missing foe blocking arcs for a foe that is targeted by friendly enchantment and arcs are set to "ON" (vs MOUSEOVER). 2018-12-25 10:04:19 -06:00
Tim Scott
44e181981d Update targeting overlay to draw target arrow for stack top when "On" (no initial mouse over required).
Prevent identical target arrows from being added to the arc lists which was causing a minor graphics glitch.
2018-12-24 16:44:01 -06:00
KrazyTheFox
52b659e902 Implement initial version of random edition selection 2018-10-17 20:40:01 -04:00
Rob Schnautz
fb8bfdc449 back matter, premium Dread Defiler 2018-04-28 00:31:30 +00:00
Rob Schnautz
06c164a013 back matter, premium Markov Dreadknight 2018-04-28 00:27:00 +00:00
Rob Schnautz
fe481d40e2 back matter, premium Soul Swallower 2018-04-28 00:24:42 +00:00
Rob Schnautz
abc42d5a32 back matter, premium Drogskol Cavalry 2018-04-28 00:22:37 +00:00
Rob Schnautz
308f525803 back matter, premium Flameblade Angel 2018-04-28 00:20:54 +00:00
Rob Schnautz
f673397c77 back matter 2018-04-28 00:13:10 +00:00
Rob Schnautz
cd6831eb2d back matter 2018-04-28 00:10:48 +00:00
Rob Schnautz
dde09633d9 back matter, premium Ivorytusk Fortress 2018-04-28 00:08:20 +00:00
Rob Schnautz
af94d5e9ce back matter, premium Sage of the Inward Eye 2018-04-28 00:06:45 +00:00
Rob Schnautz
3d33192efa back matter, premium Ankle Shanker 2018-04-28 00:01:28 +00:00
Rob Schnautz
371e083a0e back matter, premium Shipbreaker Kraken 2018-04-27 23:58:24 +00:00
Rob Schnautz
08bb85d58f back matter, premium Galvanoth 2018-04-27 23:56:14 +00:00
Rob Schnautz
a67c34d36d back matter, premium Krenko, Mob Boss 2018-04-27 23:53:42 +00:00
Rob Schnautz
a8dd39c5b3 back matter 2018-04-27 23:50:09 +00:00
Rob Schnautz
815f989310 back matter, premium Flayer of the Hatebound 2018-04-27 23:46:35 +00:00
Rob Schnautz
2cda73ad77 back matter 2018-04-27 23:44:11 +00:00
Rob Schnautz
bf290a84bd back matter and premium Dawnbringer Charioteers 2018-04-27 23:41:02 +00:00
2520 changed files with 19727 additions and 6589 deletions

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

3
.gitignore vendored
View File

@@ -231,3 +231,6 @@ forge-gui/tools/oracleScript.log
/release.properties
/target
/test-output
.settings
.classpath
.project

View File

@@ -0,0 +1,33 @@
Summary
(Summarize the bug encountered concisely)
Steps to reproduce
(How one can reproduce the issue - this is very important. Specific cards and specific actions especially)
Which version of Forge are you on (Release, Snapshot? Desktop, Android?)
What is the current bug behavior?
(What actually happens)
What is the expected correct behavior?
(What you should see instead)
Relevant logs and/or screenshots
(Paste/Attach your game.log from the crash - please use code blocks (```)) Also, provide screenshots of the current state.
Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
/label ~needs-investigation

View File

@@ -0,0 +1,15 @@
Summary
(Summarize the feature you wish concisely)
Example screenshots
(If this is a UI change, please provide an example screenshot of how this feature might work)
Feature type
(Where in Forge does this belong? e.g. Quest Mode, Deck Editor, Limited, Constructed, etc.)
/label ~feature request

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>forge</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -1,7 +0,0 @@
add_header=true
add_todo=false
eclipse.preferences.version=1
header_text=/*\n * Forge\: Play Magic\: the Gathering.\n * Copyright (C) 2011 Forge Team\n *\n * This program is free software\: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n * \n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n * \n * You should have received a copy of the GNU General Public License\n * along with this program. If not, see <http\://www.gnu.org/licenses/>.\n */
project_specific_settings=true
replacements=<?xml version\="1.0" standalone\="yes"?>\n\n<replacements>\n<replacement key\="get" scope\="1" mode\="0">Gets the</replacement>\n<replacement key\="set" scope\="1" mode\="0">Sets the</replacement>\n<replacement key\="add" scope\="1" mode\="0">Adds the</replacement>\n<replacement key\="edit" scope\="1" mode\="0">Edits the</replacement>\n<replacement key\="remove" scope\="1" mode\="0">Removes the</replacement>\n<replacement key\="init" scope\="1" mode\="0">Inits the</replacement>\n<replacement key\="parse" scope\="1" mode\="0">Parses the</replacement>\n<replacement key\="create" scope\="1" mode\="0">Creates the</replacement>\n<replacement key\="build" scope\="1" mode\="0">Builds the</replacement>\n<replacement key\="is" scope\="1" mode\="0">Checks if is</replacement>\n<replacement key\="print" scope\="1" mode\="0">Prints the</replacement>\n<replacement key\="has" scope\="1" mode\="0">Checks for</replacement>\n</replacements>\n\n
visibility_private=false

View File

@@ -1,2 +0,0 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@@ -1,284 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
org.eclipse.jdt.core.formatter.comment.line_length=80
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=2
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
org.eclipse.jdt.core.formatter.indentation.size=4
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=120
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=space
org.eclipse.jdt.core.formatter.tabulation.size=4
org.eclipse.jdt.core.formatter.use_on_off_tags=false
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=true
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +0,0 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

230
README.md Normal file
View File

@@ -0,0 +1,230 @@
# Forge
Gitlab repo is found [here](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)
Discord channel [here](https://discordapp.com/channels/267367946135928833/267742313390931968)
# Requirements / Tools
- Java IDE such as IntelliJ or Eclipse
- Java JDK 8 or later
- Git
- Git client (optional)
- Maven
- Gitlab account
- Libgdx (optional: familiarity with this library is helpful for mobile platform development)
- Android SDK (optional: for Android releases)
- RoboVM (optional: for iOS releases) (TBD: Current status of support by libgdx)
# Project Quick Setup
- Log in to gitlab with your user account and fork the project.
- Clone your forked project to your local machine
- 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 includes Maven integration so a separate install is not necessary. For other IDEs, your mileage may vary.
## Project Setup
- Follow the instructions for cloning from Gitlab. You'll need a Gitlab account setup and an SSH key defined.
If you are on a Windows machine you can use Putty with TortoiseGit for SSH keys. Run puttygen.exe to generate the key -- save the private key and export
the OpenSSH public key. If you just leave the dialog open, you can copy and paste the key from it to your Gitlab profile under
"SSH keys". Run pageant.exe and add the private key generated earlier. TortoiseGit will use this for accessing Gitlab.
- Fork the Forge git repo to your Gitlab account.
- Clone your forked repo to your local machine.
- Make sure the Java SDK is installed -- not just the JRE. Java 8 or newer required. If you execute `java -version` at the shell or command prompt, it should report version 1.8 or later.
- Install Eclipse 2018-12 or later for Java. Launch it.
- Create a workspace. Go to the workbench. Right-click inside of Package Explorer > Import... > Maven > Existing Maven Projects > Navigate to root path of the local forge repo and
ensure everything is checked > Finish.
- Let Eclipse run through building the project. You may be prompted for resolving any missing Maven plugins -- accept the ones offered. You may see errors appear in the "Problems" tab. These should
be automatically resolved as plug-ins are installed and Eclipse continues the build process. If this is the first time for some plug-in installs, Eclipse may prompt you to restart. Do so. Be patient
for this first time through.
- Once everything builds, all errors should disappear. You can now advance to Project launch.
## Project Launch
### Desktop
This is the standard configuration used for releasing to Windows / Linux / MacOS.
- Right-click on forge-gui-desktop > Run As... > Java Application > "Main - forge.view" > Ok
- The familiar Forge splash screen, etc. should appear. Enjoy!
### 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.
- Right-click on forge-gui-mobile-dev > Run As... > Java Application > "Main - forge.app" > Ok.
- A view similar to a mobile phone should appear. Enjoy!
## 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
Reference SO for obtaining a specific release: https://stackoverflow.com/questions/27043522/where-can-i-download-an-older-version-of-the-android-sdk
#### 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
TBD
### 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
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
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
Note that this will populate additional tools in the Android SDK install path extracted above.
### 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/.
- Go to the Android SDK install path. Rename the tools/proguard/ path to tools/proguard4.7/.
- 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
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.
1) Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
- On the Main tab, set Goals: clean install
2) Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
3) Right-click on the forge-gui-android project. Run as.. > Maven build...
- On the Main tab, set Goals: install, Profiles: android-debug
- On the Environment tab, you may need to define the variable ANDROID_HOME with the value containing the path to your Android SDK installation. For example, Variable: ANDROID_HOME, Value: Your Android SDK install path here.
4) Run the forge-gui-android Maven build. This may take a few minutes. If everything worked, you should see "BUILD SUCCESS" in the Console View.
Assuming you got this far, you should have an Android forge-android-[version].apk in the forge-gui-android/target path.
### Android Deploy
You'll need to have the Android SDK install path platform-tools/ path in your command search path to easily deploy builds.
- Open a command prompt. Navigate to the forge-gui-android/target/ path.
- Connect your Android device to your dev machine.
- Ensure the device is visible using `adb devices`
- Remove the old Forge install if present: `adb uninstall forge.app`
- Install the new apk: `adb install forge-android-[version].apk`
### 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
SNAPSHOT builds can be built via the Maven integration in Eclipse.
1) Create a Maven build for the forge top-level project. Right-click on the forge project. Run as.. > Maven build...
- On the Main tab, set Goals: clean install, set Profiles: windows-linux
2) Run forge Maven build. If everything built, you should see "BUILD SUCCESS" in the Console View.
The resulting snapshot will be found at: forge-gui-desktop/target/forge-gui-desktop-[version]-SNAPSHOT
# IntelliJ
TBD
# Card Scripting
Visit [this page](https://www.slightlymagic.net/wiki/Forge_API) for information on scripting.
Card scripting resources are found in the forge-gui/res/ path.
# General Notes
## Project Hierarchy
Forge is divided into 4 primary projects with additional projects that target specific platform releases. The primary projects are:
- forge-ai
- forge-core
- forge-game
- forge-gui
The platform-specific projects are:
- forge-gui-android
- forge-gui-desktop
- forge-gui-ios
- forge-gui-mobile
- forge-gui-mobile-dev
### forge-ai
### forge-core
### forge-game
### forge-gui
The forge-gui project includes the scripting resource definitions in the res/ path.
### forge-gui-android
Libgdx-based backend targeting Android. Requires Android SDK and relies on forge-gui-mobile for GUI logic.
### 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
Libgdx-based backend targeting iOS. Relies on forge-gui-mobile for GUI logic.
### 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
Libgdx backend for desktop development for mobile backends. Utilizes LWJGL. Relies on forge-gui-mobile for GUI logic.

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/forge-core"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>forge-ai</name>
<comment></comment>
<projects>
<project>forge-game</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -1,3 +0,0 @@
eclipse.preferences.version=1
encoding//src/main/java=ISO-8859-1
encoding/<project>=UTF-8

View File

@@ -1,5 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7

View File

@@ -1,4 +0,0 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

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

View File

@@ -1211,7 +1211,7 @@ public class AiController {
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
ApiType api = sa.getApi();
// Abilities without api may also use this routine, However they should provide a unique mode value
// Abilities without api may also use this routine, However they should provide a unique mode value ?? How could this work?
if (api == null) {
String exMsg = String.format("AI confirmAction does not know what to decide about %s mode (api is null).",
mode);

View File

@@ -821,6 +821,8 @@ public class AiCostDecision extends CostDecisionMakerBase {
}
}
}
} else if (sVar.equals("Count$xPaid")) {
c = AbilityUtils.calculateAmount(source, "PayX", null);
} else {
c = AbilityUtils.calculateAmount(source, amount, ability);
}

View File

@@ -73,6 +73,7 @@ public class ComputerUtil {
public static boolean handlePlayingSpellAbility(final Player ai, SpellAbility sa, final Game game, Runnable chooseTargets) {
game.getStack().freezeStack();
final Card source = sa.getHostCard();
source.setSplitStateToPlayAbility(sa);
if (sa.isSpell() && !source.isCopiedSpell()) {
if (source.getType().hasStringType("Arcane")) {
@@ -2847,7 +2848,7 @@ public class ComputerUtil {
repParams.put("Source", source);
List<ReplacementEffect> list = player.getGame().getReplacementHandler().getReplacementList(repParams,
ReplacementLayer.None);
ReplacementLayer.Other);
if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "NoLife"))) {
return false;
@@ -2878,7 +2879,7 @@ public class ComputerUtil {
repParams.put("Source", source);
List<ReplacementEffect> list = player.getGame().getReplacementHandler().getReplacementList(repParams,
ReplacementLayer.None);
ReplacementLayer.Other);
if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "NoLife"))) {
// no life gain is not negative

View File

@@ -95,9 +95,15 @@ public class ComputerUtilAbility {
public static List<SpellAbility> getOriginalAndAltCostAbilities(final List<SpellAbility> originList, final Player player) {
final List<SpellAbility> newAbilities = Lists.newArrayList();
for (SpellAbility sa : originList) {
sa.setActivatingPlayer(player);
List<SpellAbility> originListWithAddCosts = Lists.newArrayList();
for (SpellAbility sa : originList) {
// If this spell has alternative additional costs, add them instead of the unmodified SA itself
sa.setActivatingPlayer(player);
originListWithAddCosts.addAll(GameActionUtil.getAdditionalCostSpell(sa));
}
for (SpellAbility sa : originListWithAddCosts) {
// determine which alternative costs are cheaper than the original and prioritize them
List<SpellAbility> saAltCosts = GameActionUtil.getAlternativeCosts(sa, player);
List<SpellAbility> priorityAltSa = Lists.newArrayList();

View File

@@ -1455,7 +1455,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && blocker != null && blocker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
continue;
}
@@ -1692,7 +1692,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && blocker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
continue;
}
@@ -2590,7 +2590,7 @@ public class ComputerUtilCombat {
// repParams.put("PreventMap", preventMap);
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(repParams,
ReplacementLayer.None);
ReplacementLayer.Other);
return !list.isEmpty();
}

View File

@@ -74,6 +74,7 @@ public abstract class GameState {
private final Map<Card, Integer> cardToEnchantPlayerId = new HashMap<>();
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, String> cardToChosenType = new HashMap<>();
private final Map<Card, List<String>> cardToRememberedId = new HashMap<>();
private final Map<Card, List<String>> cardToImprintedId = new HashMap<>();
@@ -98,6 +99,8 @@ public abstract class GameState {
private int turn = 1;
private boolean removeSummoningSickness = false;
// Targeting for precast spells in a game state (mostly used by Puzzle Mode game states)
private final int TARGET_NONE = -1; // untargeted spell (e.g. Joraga Invocation)
private final int TARGET_HUMAN = -2;
@@ -211,6 +214,10 @@ public abstract class GameState {
// Remember the IDs of imprinted cards
cardsReferencedByID.add(i);
}
for (Card i : card.getChosenCards()) {
// Remember the IDs of chosen cards
cardsReferencedByID.add(i);
}
if (game.getCombat() != null && game.getCombat().isAttacking(card)) {
// Remember the IDs of attacked planeswalkers
GameEntity def = game.getCombat().getDefenderByAttacker(card);
@@ -312,6 +319,17 @@ public abstract class GameState {
newText.append("|NamedCard:").append(c.getNamedCard());
}
List<String> chosenCardIds = Lists.newArrayList();
for (Object obj : c.getChosenCards()) {
if (obj instanceof Card) {
int id = ((Card)obj).getId();
chosenCardIds.add(String.valueOf(id));
}
}
if (!chosenCardIds.isEmpty()) {
newText.append("|ChosenCards:").append(TextUtil.join(chosenCardIds, ","));
}
List<String> rememberedCardIds = Lists.newArrayList();
for (Object obj : c.getRemembered()) {
if (obj instanceof Card) {
@@ -432,6 +450,10 @@ public abstract class GameState {
turn = Integer.parseInt(categoryValue);
}
else if (categoryName.equals("removesummoningsickness")) {
removeSummoningSickness = categoryValue.equalsIgnoreCase("true");
}
else if (categoryName.endsWith("life")) {
if (isHuman)
humanLife = Integer.parseInt(categoryValue);
@@ -552,6 +574,7 @@ public abstract class GameState {
cardToExiledWithId.clear();
markedDamage.clear();
cardToChosenClrs.clear();
cardToChosenCards.clear();
cardToChosenType.clear();
cardToScript.clear();
cardAttackMap.clear();
@@ -605,6 +628,12 @@ public abstract class GameState {
game.getPhaseHandler().devAdvanceToPhase(advPhase);
}
if (removeSummoningSickness) {
for (Card card : game.getCardsInGame()) {
card.setSickness(false);
}
}
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
}
@@ -947,6 +976,12 @@ public abstract class GameState {
Card c = entry.getKey();
c.setNamedCard(entry.getValue());
}
// Chosen cards
for (Entry<Card, CardCollection> entry : cardToChosenCards.entrySet()) {
Card c = entry.getKey();
c.setChosenCards(entry.getValue());
}
}
private void handleCardAttachments() {
@@ -1047,7 +1082,6 @@ public abstract class GameState {
zone.setCards(kv.getValue());
}
}
}
/**
@@ -1143,6 +1177,13 @@ public abstract class GameState {
cardToChosenClrs.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
} else if (info.startsWith("ChosenType:")) {
cardToChosenType.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("ChosenCards:")) {
CardCollection chosen = new CardCollection();
String[] idlist = info.substring(info.indexOf(':') + 1).split(",");
for (String id : idlist) {
chosen.add(idToCard.get(Integer.parseInt(id)));
}
cardToChosenCards.put(c, chosen);
} else if (info.startsWith("NamedCard:")) {
cardToNamedCard.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("ExecuteScript:")) {

View File

@@ -162,10 +162,36 @@ public class PlayerControllerAi extends PlayerController {
@Override
public <T extends GameEntity> List<T> chooseEntitiesForEffect(
FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title,
FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title,
Player targetedPlayer) {
// this isn't used
return null;
if (delayedReveal != null) {
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
FCollection<T> remaining = new FCollection<T>(optionList);
List<T> selecteds = new ArrayList<T>();
T selected;
do {
selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer);
if ( selected != null ) {
remaining.remove(selected);
selecteds.add(selected);
}
} while ( (selected != null ) && (selecteds.size() < max) );
return selecteds;
}
@Override
public <T extends GameEntity> List<T> chooseFromTwoListsForEffect(FCollectionView<T> optionList1, FCollectionView<T> optionList2,
boolean optional, DelayedReveal delayedReveal, SpellAbility sa, String title, Player targetedPlayer) {
if (delayedReveal != null) {
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
T selected1 = chooseSingleEntityForEffect(optionList1, null, sa, title, optional, targetedPlayer);
T selected2 = chooseSingleEntityForEffect(optionList2, null, sa, title, optional || selected1!=null, targetedPlayer);
List<T> selecteds = new ArrayList<T>();
if ( selected1 != null ) { selecteds.add(selected1); }
if ( selected2 != null ) { selecteds.add(selected2); }
return selecteds;
}
@Override
@@ -1090,7 +1116,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public List<Card> chooseCardsForZoneChange(
ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList,
ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, int min, int max,
DelayedReveal delayedReveal, String selectPrompt, Player decider) {
// this isn't used
return null;
@@ -1164,4 +1190,10 @@ public class PlayerControllerAi extends PlayerController {
return chosenOptCosts;
}
@Override
public boolean confirmMulliganScry(Player p) {
// Always true?
return true;
}
}

View File

@@ -233,8 +233,15 @@ public class AttachAi extends SpellAbilityAi {
boolean alternativeConsiderations = hasFloatMana || willDiscardNow || willDieNow || willRespondToStack || willCastAtEOT || willCastEarly;
if (!alternativeConsiderations && (combat == null || game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) || (!combat.isAttacking(attachTarget) && !combat.isBlocking(attachTarget))) {
return false;
if (!alternativeConsiderations) {
if (combat == null ||
game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
return false;
}
if (!(combat.isAttacking(attachTarget) || combat.isBlocking(attachTarget))) {
return false;
}
}
return true;

View File

@@ -311,13 +311,20 @@ public class CountersPutAi extends SpellAbilityAi {
return false;
}
if (sa.hasParam("Adapt") && source.getCounters(CounterType.P1P1) > 0) {
return false;
}
// TODO handle proper calculation of X values based on Cost
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
if (sa.hasParam("Adapt")) {
Game game = ai.getGame();
Combat combat = game.getCombat();
if (!source.canReceiveCounters(CounterType.P1P1) || source.getCounters(CounterType.P1P1) > 0) {
return false;
} else if (combat != null && ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
return doCombatAdaptLogic(source, amount, combat);
}
}
if ("Fight".equals(logic)) {
int nPump = 0;
if (type.equals("P1P1")) {
@@ -1049,4 +1056,39 @@ public class CountersPutAi extends SpellAbilityAi {
return false;
}
private boolean doCombatAdaptLogic(Card source, int amount, Combat combat) {
if (combat.isAttacking(source)) {
if (!combat.isBlocked(source)) {
return true;
} else {
for (Card blockedBy : combat.getBlockers(source)) {
if (blockedBy.getNetToughness() > source.getNetPower()
&& blockedBy.getNetToughness() <= source.getNetPower() + amount) {
return true;
}
}
int totBlkPower = Aggregates.sum(combat.getBlockers(source), CardPredicates.Accessors.fnGetNetPower);
if (source.getNetToughness() <= totBlkPower
&& source.getNetToughness() + amount > totBlkPower) {
return true;
}
}
} else if (combat.isBlocking(source)) {
for (Card blocked : combat.getAttackersBlockedBy(source)) {
if (blocked.getNetToughness() > source.getNetPower()
&& blocked.getNetToughness() <= source.getNetPower() + amount) {
return true;
}
}
int totAtkPower = Aggregates.sum(combat.getAttackersBlockedBy(source), CardPredicates.Accessors.fnGetNetPower);
if (source.getNetToughness() <= totAtkPower
&& source.getNetToughness() + amount > totAtkPower) {
return true;
}
}
return false;
}
}

View File

@@ -11,6 +11,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.*;
import forge.game.cost.Cost;
import forge.game.cost.CostPart;
import forge.game.cost.CostPartMana;
import forge.game.cost.CostRemoveCounter;
import forge.game.keyword.Keyword;
@@ -286,6 +287,18 @@ public class DamageDealAi extends DamageAiBase {
}
}
if ("XCountersDamage".equals(logic) && sa.getPayCosts() != null) {
// Check to ensure that we have enough counters to remove per the defined PayX
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
if (source.getCounters(((CostRemoveCounter) part).counter) < Integer.valueOf(source.getSVar("PayX"))) {
return false;
}
break;
}
}
}
return true;
}
@@ -635,10 +648,9 @@ public class DamageDealAi extends DamageAiBase {
if (c != null && !this.shouldTgtP(ai, sa, dmg, noPrevention, true)) {
tcs.add(c);
if (divided) {
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
if (assignedDamage <= dmg) {
tgt.addDividedAllocation(c, assignedDamage);
}
int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention);
assignedDamage = Math.min(dmg, assignedDamage);
tgt.addDividedAllocation(c, assignedDamage);
dmg = dmg - assignedDamage;
if (dmg <= 0) {
break;
@@ -1080,7 +1092,7 @@ public class DamageDealAi extends DamageAiBase {
continue; // not a toughness debuff
}
}
if (StringUtils.isNumeric(dmgDef) && ab.canPlay()) { // currently doesn't work for X and other dependent costs
if (StringUtils.isNumeric(dmgDef)) { // currently doesn't work for X and other dependent costs
if (sa.usesTargeting() && ab.usesTargeting()) {
// Ensure that the chained spell can target at least the same things (or more) as the current one
TargetRestrictions tgtSa = sa.getTargetRestrictions();

View File

@@ -51,6 +51,13 @@ public class LifeLoseAi extends SpellAbilityAi {
if (tgtPlayers.contains(ai) && amount > 0 && amount + 3 > ai.getLife()) {
return false;
}
if (sa.usesTargeting()) {
if (!doTgt(ai, sa, false)) {
return false;
}
}
return true;
}

View File

@@ -113,7 +113,7 @@ public class ManifestAi extends SpellAbilityAi {
repParams.put("Origin", ZoneType.Library);
repParams.put("Destination", ZoneType.Battlefield);
repParams.put("Source", sa.getHostCard());
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(repParams, ReplacementLayer.None);
List<ReplacementEffect> list = game.getReplacementHandler().getReplacementList(repParams, ReplacementLayer.Other);
if (!list.isEmpty()) {
return false;
}

View File

@@ -1,8 +1,6 @@
package forge.ai.ability;
import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi;
import forge.ai.*;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CounterType;
@@ -26,12 +24,21 @@ public class TapAi extends TapAiBase {
if (turn.isOpponentOf(ai) && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
// Tap things down if it's Human's turn
} else if (turn == ai && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
// Tap creatures down if in combat -- handled in tapPrefTargeting().
} else if (SpellAbilityAi.isSorcerySpeed(sa)) {
// Cast it if it's a sorcery.
} else if (turn.equals(ai)) {
if (SpellAbilityAi.isSorcerySpeed(sa) && phase.getPhase().isBefore(PhaseType.COMBAT_BEGIN)) {
// Cast it if it's a sorcery.
} else if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
// Aggro Brains are willing to use TapEffects aggressively instead of defensively
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
if (!aic.getBooleanProperty(AiProps.PLAY_AGGRO)) {
return false;
}
} else {
// Don't tap down after blockers
return false;
}
} else if (!SpellAbilityAi.playReusable(ai, sa)){
// Generally don't want to tap things with an Instant during AI turn outside of combat
// Generally don't want to tap things with an Instant during Players turn outside of combat
return false;
}

View File

@@ -8,8 +8,10 @@ import forge.game.GameEntity;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.effects.TokenEffect;
import forge.game.card.*;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.token.TokenInfo;
import forge.game.combat.Combat;
import forge.game.cost.CostPart;
@@ -58,20 +60,9 @@ public class TokenAi extends SpellAbilityAi {
private void readParameters(final SpellAbility mapParams) {
this.tokenAmount = mapParams.getParamOrDefault("TokenAmount", "1");
TokenEffect effect = new TokenEffect();
this.actualToken = effect.loadTokenPrototype(mapParams);
this.actualToken = TokenInfo.getProtoType(mapParams.getParam("TokenScript"), mapParams);
if (actualToken == null) {
String[] keywords;
if (mapParams.hasParam("TokenKeywords")) {
// TODO: Change this Split to a semicolon or something else
keywords = mapParams.getParam("TokenKeywords").split("<>");
} else {
keywords = new String[0];
}
this.tokenPower = mapParams.getParam("TokenPower");
this.tokenToughness = mapParams.getParam("TokenToughness");
} else {
@@ -340,6 +331,7 @@ public class TokenAi extends SpellAbilityAi {
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
// TODO: AILogic
readParameters(sa); // remember to call this somewhere!
Combat combat = ai.getGame().getCombat();
// TokenAttacking
if (combat != null && sa.hasParam("TokenAttacking")) {
@@ -359,6 +351,7 @@ public class TokenAi extends SpellAbilityAi {
@Override
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
// TODO: AILogic
readParameters(sa); // remember to call this somewhere!
Combat combat = ai.getGame().getCombat();
// TokenAttacking
if (combat != null && sa.hasParam("TokenAttacking")) {
@@ -389,6 +382,7 @@ public class TokenAi extends SpellAbilityAi {
* @param sa Token SpellAbility
* @return token creature created by ability
*/
@Deprecated
public static Card spawnToken(Player ai, SpellAbility sa) {
return spawnToken(ai, sa, false);
}
@@ -401,9 +395,17 @@ public class TokenAi extends SpellAbilityAi {
* @return token creature created by ability
*/
// TODO Is this just completely copied from TokenEffect? Let's just call that thing
@Deprecated
public static Card spawnToken(Player ai, SpellAbility sa, boolean notNull) {
final Card host = sa.getHostCard();
Card result = TokenInfo.getProtoType(sa.getParam("TokenScript"), sa);
if (result != null) {
result.setController(ai, 0);
return result;
}
String[] tokenKeywords = sa.hasParam("TokenKeywords") ? sa.getParam("TokenKeywords").split("<>") : new String[0];
String tokenPower = sa.getParam("TokenPower");
String tokenToughness = sa.getParam("TokenToughness");

View File

@@ -310,7 +310,8 @@ public class GameCopier {
newCard.setManifested(true);
// TODO: Should be able to copy other abilities...
if (isCreature && hasManaCost) {
newCard.addSpellAbility(CardFactoryUtil.abilityManifestFaceUp(newCard, newCard.getManaCost()));
newCard.getState(CardStateName.Original).addSpellAbility(
CardFactoryUtil.abilityManifestFaceUp(newCard, newCard.getManaCost()));
}
}
}

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>forge-core</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -1,4 +0,0 @@
eclipse.preferences.version=1
encoding//src/main/java=ISO-8859-1
encoding//src/test/java=ISO-8859-1
encoding/<project>=UTF-8

View File

@@ -1,5 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7

View File

@@ -1,4 +0,0 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>1.6.20-SNAPSHOT</version>
<version>1.6.23</version>
</parent>
<artifactId>forge-core</artifactId>

View File

@@ -21,7 +21,6 @@ import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.CardDb.SetPreference;
import forge.deck.CardPool;
@@ -31,7 +30,6 @@ import forge.util.*;
import forge.util.storage.StorageBase;
import forge.util.storage.StorageReaderBase;
import forge.util.storage.StorageReaderFolder;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
@@ -123,17 +121,17 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
private boolean smallSetOverride = false;
private String boosterMustContain = "";
private final CardInSet[] cards;
private final String[] tokenNormalized;
private final Map<String, Integer> tokenNormalized;
private int boosterArts = 1;
private SealedProduct.Template boosterTpl = null;
private CardEdition(CardInSet[] cards) {
this.cards = cards;
tokenNormalized = null;
tokenNormalized = new HashMap<>();
}
private CardEdition(CardInSet[] cards, String[] tokens) {
private CardEdition(CardInSet[] cards, Map<String, Integer> tokens) {
this.cards = cards;
this.tokenNormalized = tokens;
}
@@ -191,6 +189,8 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public String getBoosterMustContain() { return boosterMustContain; }
public CardInSet[] getCards() { return cards; }
public Map<String, Integer> getTokens() { return tokenNormalized; };
public static final Function<CardEdition, String> FN_GET_CODE = new Function<CardEdition, String>() {
@Override
public String apply(final CardEdition arg1) {
@@ -261,7 +261,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
protected CardEdition read(File file) {
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
List<String> tokenNormalized = new ArrayList<>();
Map<String, Integer> tokenNormalized = new HashMap<>();
List<CardEdition.CardInSet> processedCards = new ArrayList<>();
if (contents.containsKey("cards")) {
for(String line : contents.get("cards")) {
@@ -290,13 +290,17 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
if (StringUtils.isBlank(line))
continue;
tokenNormalized.add(line);
if (!tokenNormalized.containsKey(line)) {
tokenNormalized.put(line, 1);
} else {
tokenNormalized.put(line, tokenNormalized.get(line) + 1);
}
}
}
CardEdition res = new CardEdition(
processedCards.toArray(new CardInSet[processedCards.size()]),
tokenNormalized.toArray(new String[tokenNormalized.size()])
tokenNormalized
);
FileSection section = FileSection.parse(contents.get("metadata"), "=");

View File

@@ -581,12 +581,11 @@ public final class CardRulesPredicates {
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardType.CoreType.Vanguard);
public static final Predicate<CardRules> IS_CONSPIRACY = CardRulesPredicates.coreType(true, CardType.CoreType.Conspiracy);
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardType.CoreType.Land);
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(Presets.IS_CREATURE, Presets.IS_LAND));
public static final Predicate<CardRules> CAN_BE_BRAWL_COMMANDER = Predicates.or(Presets.IS_PLANESWALKER,
Predicates.and(Presets.IS_CREATURE, Presets.IS_LEGENDARY));
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
public static final Predicate<CardRules> IS_NONCREATURE_SPELL_FOR_GENERATOR = com.google.common.base.Predicates
/** The Constant IS_NON_CREATURE_SPELL. **/
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = com.google.common.base.Predicates
.or(Presets.IS_SORCERY, Presets.IS_INSTANT, Presets.IS_PLANESWALKER, Presets.IS_ENCHANTMENT,
Predicates.and(Presets.IS_ARTIFACT, Predicates.not(Presets.IS_CREATURE)));

View File

@@ -20,6 +20,8 @@ package forge.deck;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import forge.StaticData;
import forge.card.CardRules;
import forge.card.CardRulesPredicates;
@@ -47,7 +49,7 @@ public enum DeckFormat {
QuestDeck ( Range.between(40, Integer.MAX_VALUE), Range.between(0, 15), 4),
Limited ( Range.between(40, Integer.MAX_VALUE), null, Integer.MAX_VALUE),
Commander ( Range.is(99), Range.between(0, 10), 1, new Predicate<CardRules>() {
private final Set<String> bannedCards = new HashSet<String>(Arrays.asList(
private final Set<String> bannedCards = ImmutableSet.of(
"Adriana's Valor", "Advantageous Proclamation", "Amulet of Quoz", "Ancestral Recall", "Assemble the Rank and Vile",
"Backup Plan", "Balance", "Biorhythm", "Black Lotus", "Brago's Favor", "Braids, Cabal Minion", "Bronze Tablet",
"Channel", "Chaos Orb", "Coalition Victory", "Contract from Below", "Darkpact", "Demonic Attorney", "Double Stroke",
@@ -59,7 +61,7 @@ public enum DeckFormat {
"Rebirth", "Recurring Nightmare", "Rofellos, Llanowar Emissary", "Secret Summoning", "Secrets of Paradise",
"Sentinel Dispatch", "Shahrazad", "Sovereign's Realm", "Summoner's Bond", "Sundering Titan", "Sway of the Stars",
"Sylvan Primordial", "Tempest Efreet", "Time Vault", "Time Walk", "Timmerian Fiends", "Tinker", "Tolarian Academy",
"Trade Secrets", "Unexpected Potential", "Upheaval", "Weight Advantage", "Worldfire", "Worldknit", "Yawgmoth's Bargain"));
"Trade Secrets", "Unexpected Potential", "Upheaval", "Weight Advantage", "Worldfire", "Worldknit", "Yawgmoth's Bargain");
@Override
public boolean apply(CardRules rules) {
if (bannedCards.contains(rules.getName())) {
@@ -70,8 +72,8 @@ public enum DeckFormat {
}),
Pauper ( Range.is(60), Range.between(0, 10), 1),
Brawl ( Range.is(59), Range.between(0, 15), 1, null, new Predicate<PaperCard>() {
private final Set<String> bannedCards = new HashSet<String>(Arrays.asList(
"Baral, Chief of Compliance","Smuggler's Copter","Sorcerous Spyglass"));
private final Set<String> bannedCards = ImmutableSet.of(
"Baral, Chief of Compliance","Smuggler's Copter","Sorcerous Spyglass");
@Override
public boolean apply(PaperCard card) {
//why do we need to hard code the bannings here - they are defined in the GameFormat predicate used below
@@ -81,7 +83,7 @@ public enum DeckFormat {
return StaticData.instance() == null ? false : StaticData.instance().getBrawlPredicate().apply(card);
}
}) {
private final ImmutableSet<String> bannedCommanders = ImmutableSet.of("Baral, Chief of Compliance");
private final Set<String> bannedCommanders = ImmutableSet.of("Baral, Chief of Compliance");
@Override
public boolean isLegalCommander(CardRules rules) {
@@ -89,11 +91,11 @@ public enum DeckFormat {
}
},
TinyLeaders ( Range.is(49), Range.between(0, 10), 1, new Predicate<CardRules>() {
private final Set<String> bannedCards = new HashSet<String>(Arrays.asList(
private final Set<String> bannedCards = ImmutableSet.of(
"Ancestral Recall", "Balance", "Black Lotus", "Black Vise", "Channel", "Chaos Orb", "Contract From Below", "Counterbalance", "Darkpact", "Demonic Attorney", "Demonic Tutor", "Earthcraft", "Edric, Spymaster of Trest", "Falling Star",
"Fastbond", "Flash", "Goblin Recruiter", "Grindstone", "Hermit Druid", "Imperial Seal", "Jeweled Bird", "Karakas", "Library of Alexandria", "Mana Crypt", "Mana Drain", "Mana Vault", "Metalworker", "Mind Twist", "Mishra's Workshop",
"Mox Emerald", "Mox Jet", "Mox Pearl", "Mox Ruby", "Mox Sapphire", "Necropotence", "Shahrazad", "Skullclamp", "Sol Ring", "Strip Mine", "Survival of the Fittest", "Sword of Body and Mind", "Time Vault", "Time Walk", "Timetwister",
"Timmerian Fiends", "Tolarian Academy", "Umezawa's Jitte", "Vampiric Tutor", "Wheel of Fortune", "Yawgmoth's Will"));
"Timmerian Fiends", "Tolarian Academy", "Umezawa's Jitte", "Vampiric Tutor", "Wheel of Fortune", "Yawgmoth's Will");
@Override
public boolean apply(CardRules rules) {
@@ -112,7 +114,7 @@ public enum DeckFormat {
return true;
}
}) {
private final ImmutableSet<String> bannedCommanders = ImmutableSet.of("Derevi, Empyrial Tactician", "Erayo, Soratami Ascendant", "Rofellos, Llanowar Emissary");
private final Set<String> bannedCommanders = ImmutableSet.of("Derevi, Empyrial Tactician", "Erayo, Soratami Ascendant", "Rofellos, Llanowar Emissary");
@Override
public boolean isLegalCommander(CardRules rules) {
@@ -141,13 +143,6 @@ public enum DeckFormat {
private final static String ADVPROCLAMATION = "Advantageous Proclamation";
private final static String SOVREALM = "Sovereign's Realm";
private static final List<String> limitExceptions = Arrays.asList(
new String[]{"Relentless Rats", "Shadowborn Apostle", "Rat Colony"});
public static List<String> getLimitExceptions(){
return limitExceptions;
}
private DeckFormat(Range<Integer> mainRange0, Range<Integer> sideRange0, int maxCardCopies0, Predicate<CardRules> cardPoolFilter0, Predicate<PaperCard> paperCardPoolFilter0) {
mainRange = mainRange0;
sideRange = sideRange0;
@@ -342,7 +337,6 @@ public enum DeckFormat {
//basic lands, Shadowborn Apostle, Relentless Rats and Rat Colony
final CardPool allCards = deck.getAllCardsInASinglePool(hasCommander());
final ImmutableSet<String> limitExceptions = ImmutableSet.of("Relentless Rats", "Shadowborn Apostle", "Rat Colony");
// should group all cards by name, so that different editions of same card are really counted as the same card
for (final Entry<String, Integer> cp : Aggregates.groupSumBy(allCards, PaperCard.FN_GET_NAME)) {
@@ -351,8 +345,7 @@ public enum DeckFormat {
return TextUtil.concatWithSpace("contains the nonexisting card", cp.getKey());
}
final boolean canHaveMultiple = simpleCard.getRules().getType().isBasicLand() || limitExceptions.contains(cp.getKey());
if (!canHaveMultiple && cp.getValue() > maxCopies) {
if (!canHaveAnyNumberOf(simpleCard) && cp.getValue() > maxCopies) {
return TextUtil.concatWithSpace("must not contain more than", String.valueOf(maxCopies), "copies of the card", cp.getKey());
}
}
@@ -370,6 +363,12 @@ public enum DeckFormat {
return null;
}
public static boolean canHaveAnyNumberOf(final IPaperCard icard) {
return icard.getRules().getType().isBasicLand()
|| Iterables.contains(icard.getRules().getMainPart().getKeywords(),
"A deck can have any number of cards named CARDNAME.");
}
public static String getPlaneSectionConformanceProblem(final CardPool planes) {
//Must contain at least 10 planes/phenomenons, but max 2 phenomenons. Singleton.
if (planes == null || planes.countAll() < 10) {

View File

@@ -102,6 +102,7 @@ public class DeckRecognizer {
// Pattern.compile("(.*)[^A-Za-wyz]*\\s+([\\d]{1,2})");
private static final Pattern SEARCH_NUMBERS_IN_FRONT = Pattern.compile("([\\d]{1,2})[^A-Za-wyz]*\\s+(.*)");
//private static final Pattern READ_SEPARATED_EDITION = Pattern.compile("[[\\(\\{]([a-zA-Z0-9]){1,3})[]*\\s+(.*)");
private static final Pattern SEARCH_SINGLE_SLASH = Pattern.compile("(?<=[^/])\\s*/\\s*(?=[^/])");
private final SetPreference useLastSet;
private final ICardDatabase db;
@@ -125,7 +126,10 @@ public class DeckRecognizer {
return new Token(TokenType.Comment, 0, rawLine);
}
final char smartQuote = (char) 8217;
final String line = rawLine.trim().replace(smartQuote, '\'');
String line = rawLine.trim().replace(smartQuote, '\'');
// Some websites export split card names with a single slash. Replace with double slash.
line = SEARCH_SINGLE_SLASH.matcher(line).replaceFirst(" // ");
Token result = null;
final Matcher foundNumbersInFront = DeckRecognizer.SEARCH_NUMBERS_IN_FRONT.matcher(line);

View File

@@ -100,7 +100,7 @@ public abstract class DeckGeneratorBase {
trace.append("Creatures to add:").append(creatCnt).append("\n");
addCmcAdjusted(creatures, creatCnt, cmcLevels);
Predicate<PaperCard> preSpells = Predicates.compose(CardRulesPredicates.Presets.IS_NONCREATURE_SPELL_FOR_GENERATOR, PaperCard.FN_GET_RULES);
Predicate<PaperCard> preSpells = Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard.FN_GET_RULES);
final Iterable<PaperCard> spells = Iterables.filter(cards, preSpells);
final int spellCnt = (int) Math.ceil(getSpellPercentage() * size);
trace.append("Spells to add:").append(spellCnt).append("\n");

View File

@@ -5,6 +5,7 @@ import forge.card.CardEdition;
import forge.card.CardRarity;
import forge.card.CardRules;
import forge.card.ColorSet;
import forge.util.MyRandom;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
@@ -13,8 +14,9 @@ import java.util.Locale;
public class PaperToken implements InventoryItemFromSet, IPaperCard {
private String name;
private CardEdition edition;
private String imageFileName;
private ArrayList<String> imageFileName = new ArrayList<>();
private CardRules card;
private int artIndex = 1;
// takes a string of the form "<colors> <power> <toughness> <name>" such as: "B 0 0 Germ"
public static String makeTokenFileName(String in) {
@@ -100,23 +102,28 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
build.add(edition.getCode());
}
// Should future image file names be all lower case? Instead of Up case sets?
return StringUtils.join(build, "_").toLowerCase();
return StringUtils.join(build, "_").replace('*', 'x').toLowerCase();
}
public PaperToken(final CardRules c) { this(c, null, null); }
public PaperToken(final CardRules c, final String fileName) { this(c, null, fileName); }
public PaperToken(final CardRules c, CardEdition edition) { this(c, edition, null); }
public PaperToken(final CardRules c, CardEdition edition0, String imageFileName) {
this.card = c;
this.name = c.getName();
this.edition = edition0;
if (edition != null && edition.getTokens().containsKey(imageFileName)) {
this.artIndex = edition.getTokens().get(imageFileName);
}
if (imageFileName == null) {
this.imageFileName = makeTokenFileName(c, edition0);
// This shouldn't really happen. We can just use the normalized name again for the base image name
this.imageFileName.add(makeTokenFileName(c, edition0));
} else {
String formatEdition = null == edition || CardEdition.UNKNOWN == edition ? "" : edition.getCode();
this.imageFileName = String.format("%s%s", formatEdition, imageFileName);
String formatEdition = null == edition || CardEdition.UNKNOWN == edition ? "" : "_" + edition.getCode().toLowerCase();
this.imageFileName.add(String.format("%s%s", imageFileName, formatEdition));
for(int idx = 2; idx <= this.artIndex; idx++) {
this.imageFileName.add(String.format("%s%d%s", imageFileName, idx, formatEdition));
}
}
}
@@ -124,14 +131,14 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
@Override public String toString() { return name; }
@Override public String getEdition() { return edition != null ? edition.getCode() : "???"; }
@Override public int getArtIndex() { return 0; } // This might change however
@Override public int getArtIndex() { return artIndex; }
@Override public boolean isFoil() { return false; }
@Override public CardRules getRules() { return card; }
@Override public CardRarity getRarity() { return CardRarity.None; }
// Unfortunately this is a property of token, cannot move it outside of class
public String getImageFilename() { return imageFileName; }
public String getImageFilename() { return imageFileName.get(0); }
@Override public String getItemType() { return "Token"; }
@@ -139,6 +146,7 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
@Override
public String getImageKey(boolean altState) {
return ImageKeys.TOKEN_PREFIX + imageFileName.replace(" ", "_");
int idx = MyRandom.getRandom().nextInt(artIndex);
return ImageKeys.TOKEN_PREFIX + imageFileName.get(idx).replace(" ", "_");
}
}

View File

@@ -2,7 +2,9 @@ package forge.token;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import forge.card.*;
import forge.card.CardDb;
import forge.card.CardEdition;
import forge.card.CardRules;
import forge.item.PaperToken;
import java.util.*;
@@ -37,13 +39,25 @@ public class TokenDb implements ITokenDatabase {
return getToken(tokenName, CardEdition.UNKNOWN.getName());
}
public void preloadTokens() {
for(CardEdition edition : this.editions) {
for (String name : edition.getTokens().keySet()) {
try {
getToken(name, edition.getCode());
} catch(Exception e) {
System.out.println(name + "_" + edition.getCode() + " defined in Edition file, but not defined as a token script.");
}
}
}
}
@Override
public PaperToken getToken(String tokenName, String edition) {
String fullName = String.format("%s_%s", tokenName, edition.toLowerCase());
if (!tokensByName.containsKey(fullName)) {
try {
PaperToken pt = new PaperToken(rulesByName.get(tokenName), editions.get(edition));
PaperToken pt = new PaperToken(rulesByName.get(tokenName), editions.get(edition), tokenName);
tokensByName.put(fullName, pt);
return pt;
} catch(Exception e) {
@@ -101,7 +115,7 @@ public class TokenDb implements ITokenDatabase {
@Override
public List<PaperToken> getAllTokens() {
return null;
return new ArrayList<>(tokensByName.values());
}
@Override
@@ -121,6 +135,6 @@ public class TokenDb implements ITokenDatabase {
@Override
public Iterator<PaperToken> iterator() {
return null;
return tokensByName.values().iterator();
}
}

View File

@@ -37,7 +37,8 @@ public class Localizer {
MessageFormat formatter = null;
try {
formatter = new MessageFormat(resourceBundle.getString(key.toLowerCase()), locale);
//formatter = new MessageFormat(resourceBundle.getString(key.toLowerCase()), locale);
formatter = new MessageFormat(resourceBundle.getString(key.toString()), locale);
} catch (final IllegalArgumentException | MissingResourceException e) {
e.printStackTrace();
}
@@ -86,7 +87,7 @@ public class Localizer {
resourceBundle = ResourceBundle.getBundle(languageRegionID, new Locale(splitLocale[0], splitLocale[1]), loader);
} catch (NullPointerException | MissingResourceException e) {
//If the language can't be loaded, default to US English
resourceBundle = ResourceBundle.getBundle("en-GB", new Locale("en", "GB"), loader);
resourceBundle = ResourceBundle.getBundle("en-US", new Locale("en", "US"), loader);
e.printStackTrace();
}

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="src" path="/forge-core"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>forge-game</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -1,4 +0,0 @@
eclipse.preferences.version=1
encoding//src/main/java=ISO-8859-1
encoding//src/test/java=ISO-8859-1
encoding/<project>=UTF-8

View File

@@ -1,5 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7

View File

@@ -1,4 +0,0 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@@ -6,7 +6,7 @@
<parent>
<artifactId>forge</artifactId>
<groupId>forge</groupId>
<version>1.6.20-SNAPSHOT</version>
<version>1.6.23</version>
</parent>
<artifactId>forge-game</artifactId>

View File

@@ -29,6 +29,7 @@ import forge.game.ability.effects.AttachEffect;
import forge.game.card.*;
import forge.game.event.*;
import forge.game.keyword.KeywordInterface;
import forge.game.keyword.KeywordsChange;
import forge.game.player.GameLossReason;
import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
@@ -50,9 +51,9 @@ import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
import forge.util.maps.HashMapOfLists;
import forge.util.maps.MapOfLists;
import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.*;
import java.util.Map.Entry;
/**
* Methods for common actions performed during a game.
@@ -70,10 +71,8 @@ public class GameAction {
}
public final void resetActivationsPerTurn() {
final CardCollectionView all = game.getCardsInGame();
// Reset Activations per Turn
for (final Card card : all) {
for (final Card card : game.getCardsInGame()) {
for (final SpellAbility sa : card.getAllSpellAbilities()) {
sa.getRestrictions().resetTurnActivations();
}
@@ -104,6 +103,7 @@ public class GameAction {
boolean toBattlefield = zoneTo.is(ZoneType.Battlefield);
boolean fromBattlefield = zoneFrom != null && zoneFrom.is(ZoneType.Battlefield);
boolean toHand = zoneTo.is(ZoneType.Hand);
boolean wasFacedown = c.isFaceDown();
//Rule 110.5g: A token that has left the battlefield can't move to another zone
if (c.isToken() && zoneFrom != null && !fromBattlefield && !zoneFrom.is(ZoneType.Command)) {
@@ -147,16 +147,11 @@ public class GameAction {
}
}
// Cards returned from exile face-down must be reset to their original state, otherwise
// all sort of funky shenanigans may happen later (e.g. their ETB replacement effects are set
// up on the wrong card state etc.).
if (c.isFaceDown() && (fromBattlefield || (toHand && zoneFrom.is(ZoneType.Exile)))) {
c.setState(CardStateName.Original, true);
c.runFaceupCommands();
}
// Clean up the temporary Dash SVar when the Dashed card leaves the battlefield
if (fromBattlefield && c.getSVar("EndOfTurnLeavePlay").equals("Dash")) {
// Clean up the temporary AtEOT SVar
String endofTurn = c.getSVar("EndOfTurnLeavePlay");
if (fromBattlefield && (endofTurn.equals("Dash") || endofTurn.equals("AtEOT"))) {
c.removeSVar("EndOfTurnLeavePlay");
}
@@ -189,6 +184,14 @@ public class GameAction {
lastKnownInfo = CardUtil.getLKICopy(c);
}
// Cards returned from exile face-down must be reset to their original state, otherwise
// all sort of funky shenanigans may happen later (e.g. their ETB replacement effects are set
// up on the wrong card state etc.).
if (wasFacedown && (fromBattlefield || (toHand && zoneFrom.is(ZoneType.Exile)))) {
c.setState(CardStateName.Original, true);
c.runFaceupCommands();
}
if (!c.isToken()) {
if (c.isCloned()) {
c.switchStates(CardStateName.Original, CardStateName.Cloner, false);
@@ -213,15 +216,15 @@ public class GameAction {
c.updateStateForView();
}
if (fromBattlefield && c.getCurrentStateName() != CardStateName.Original) {
copied = CardFactory.copyCard(c, false);
if (fromBattlefield && copied.getCurrentStateName() != CardStateName.Original) {
// when a card leaves the battlefield, ensure it's in its original state
// (we need to do this on the object before copying it, or it won't work correctly e.g.
// on Transformed objects)
c.setState(CardStateName.Original, false);
copied.setState(CardStateName.Original, false);
}
copied = CardFactory.copyCard(c, false);
copied.setUnearthed(c.isUnearthed());
copied.setTapped(false);
@@ -248,26 +251,25 @@ public class GameAction {
// special rule for Worms of the Earth
if (toBattlefield && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLandBattlefield)) {
// something that is already a Land cant enter the battlefield
if (c.isLand()) {
return c;
}
// check if something would be a land
Card noLandLKI = CardUtil.getLKICopy(c);
// this check needs to check if this card would be on the battlefield
noLandLKI.setLastKnownZone(zoneTo);
Card noLandLKI = c;
if (!c.isLand()) {
// check if something would be a land
noLandLKI = CardUtil.getLKICopy(c);
// this check needs to check if this card would be on the battlefield
noLandLKI.setLastKnownZone(zoneTo);
CardCollection preList = new CardCollection(noLandLKI);
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
CardCollection preList = new CardCollection(noLandLKI);
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
// fake etb counters thing, then if something changed,
// need to apply checkStaticAbilities again
if(!noLandLKI.isLand()) {
if (noLandLKI.putEtbCounters()) {
// counters are added need to check again
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
// fake etb counters thing, then if something changed,
// need to apply checkStaticAbilities again
if(!noLandLKI.isLand()) {
if (noLandLKI.putEtbCounters()) {
// counters are added need to check again
checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList);
}
}
}
if(noLandLKI.isLand()) {
// if it isn't on the Stack, it stays in that Zone
if (!c.getZone().is(ZoneType.Stack)) {
@@ -276,6 +278,7 @@ public class GameAction {
// if something would only be a land when entering the battlefield and not before
// put it into the graveyard instead
zoneTo = c.getOwner().getZone(ZoneType.Graveyard);
// reset facedown
copied.setState(CardStateName.Original, false);
copied.setManifested(false);
@@ -283,6 +286,26 @@ public class GameAction {
// not to battlefield anymore!
toBattlefield = false;
if (copied.isCloned()) {
copied.switchStates(CardStateName.Original, CardStateName.Cloner, false);
copied.setState(CardStateName.Original, false);
copied.clearStates(CardStateName.Cloner, false);
if (copied.isFlipCard()) {
copied.clearStates(CardStateName.Flipped, false);
}
if (copied.getStates().contains(CardStateName.OriginalText)) {
copied.clearStates(CardStateName.OriginalText, false);
copied.removeSVar("GainingTextFrom");
copied.removeSVar("GainingTextFromTimestamp");
}
}
if (copied.getCurrentStateName() != CardStateName.Original) {
copied.setState(CardStateName.Original, false);
}
copied.updateStateForView();
}
}
@@ -291,6 +314,33 @@ public class GameAction {
copied.getOwner().addInboundToken(copied);
}
if (toBattlefield) {
// HACK for making the RIOT enchantment look into the Future
// need to check the Keywords what it would have on the Battlefield
Card riotLKI = CardUtil.getLKICopy(copied);
riotLKI.setLastKnownZone(zoneTo);
CardCollection preList = new CardCollection(riotLKI);
checkStaticAbilities(false, Sets.newHashSet(riotLKI), preList);
List<Long> changedTimeStamps = Lists.newArrayList();
for(Map.Entry<Long, KeywordsChange> e : riotLKI.getChangedCardKeywords().entrySet()) {
if (!copied.hasChangedCardKeywords(e.getKey())) {
KeywordsChange o = e.getValue();
o.setHostCard(copied);
for (KeywordInterface k : o.getKeywords()) {
for (ReplacementEffect re : k.getReplacements()) {
// this param need to be set, otherwise in ReplaceMoved it fails
re.getMapParams().put("BypassEtbCheck", "True");
}
}
copied.addChangedCardKeywordsInternal(o, e.getKey());
changedTimeStamps.add(e.getKey());
}
}
checkStaticAbilities(false);
}
Map<String, Object> repParams = Maps.newHashMap();
repParams.put("Event", "Moved");
repParams.put("Affected", copied);
@@ -373,7 +423,7 @@ public class GameAction {
// Need to apply any static effects to produce correct triggers
checkStaticAbilities();
game.getTriggerHandler().clearInstrinsicActiveTriggers(c, zoneFrom);
game.getTriggerHandler().registerActiveTrigger(c, false);
game.getTriggerHandler().registerActiveTrigger(lastKnownInfo, false);
// do ETB counters after StaticAbilities check
if (!suppress) {
@@ -435,10 +485,14 @@ public class GameAction {
}
// rule 504.6: reveal a face-down card leaving the stack
if (zoneFrom != null && zoneTo != null && zoneFrom.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Battlefield) && c.isFaceDown()) {
if (zoneFrom != null && zoneTo != null && zoneFrom.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Battlefield) && wasFacedown) {
// FIXME: tracker freeze-unfreeze is needed here to avoid a bug with the card staying face down in the View for the reveal
boolean trackerFrozen = game.getTracker().isFrozen();
game.getTracker().unfreeze();
c.setState(CardStateName.Original, true);
reveal(new CardCollection(c), c.getOwner(), true, "Face-down card moves from the stack: ");
c.setState(CardStateName.FaceDown, true);
if (trackerFrozen) { game.getTracker().freeze(); }
}
if (fromBattlefield) {
@@ -466,19 +520,19 @@ public class GameAction {
changeZone(null, zoneTo, unmeld, position, cause, params);
}
// Reveal if face-down
if (c.isFaceDown()) {
if (wasFacedown) {
// FIXME: tracker freeze-unfreeze is needed here to avoid a bug with the card staying face down in the View for the reveal
boolean trackerFrozen = game.getTracker().isFrozen();
game.getTracker().unfreeze();
c.setState(CardStateName.Original, true);
reveal(new CardCollection(c), c.getOwner(), true, "Face-down card leaves the battlefield: ");
c.setState(CardStateName.FaceDown, true);
if (trackerFrozen) { game.getTracker().freeze(); }
copied.setState(CardStateName.Original, true);
}
unattachCardLeavingBattlefield(copied);
// Remove all changed keywords
copied.removeAllChangedText(game.getNextTimestamp());
// reset activations
for (SpellAbility ab : copied.getSpellAbilities()) {
ab.getRestrictions().resetTurnActivations();
}
} else if (toBattlefield) {
// reset timestamp in changezone effects so they have same timestamp if ETB simutaneously
copied.setTimestamp(game.getNextTimestamp());
@@ -1094,7 +1148,7 @@ public class GameAction {
if (c.isAttachedToEntity()) {
final GameEntity ge = c.getEntityAttachedTo();
if (!ge.canBeAttached(c)) {
if (!ge.canBeAttached(c, true)) {
c.unattachFromEntity(ge);
checkAgain = true;
}
@@ -1669,12 +1723,8 @@ public class GameAction {
// rule 103.4b
boolean isMultiPlayer = game.getPlayers().size() > 2;
int mulliganDelta = isMultiPlayer ? 0 : 1;
// https://magic.wizards.com/en/articles/archive/feature/checking-brawl-2018-07-09
if (game.getRules().hasAppliedVariant(GameType.Brawl) && !isMultiPlayer){
mulliganDelta = 0;
}
int mulliganDelta = isMultiPlayer || game.getRules().hasAppliedVariant(GameType.Brawl) ? 0 : 1;
boolean allKept;
do {
@@ -1690,32 +1740,17 @@ public class GameAction {
}
if (toMulligan != null && !toMulligan.isEmpty()) {
if (!isCommander) {
toMulligan = new CardCollection(p.getCardsIn(ZoneType.Hand));
for (final Card c : toMulligan) {
moveToLibrary(c, null, null);
}
try {
Thread.sleep(100); //delay for a tiny bit to give UI a chance catch up
} catch (InterruptedException e) {
e.printStackTrace();
}
p.shuffle(null);
p.drawCards(handSize[i] - mulliganDelta);
} else {
List<Card> toExile = Lists.newArrayList(toMulligan);
for (Card c : toExile) {
exile(c, null, null);
}
exiledDuringMulligans.addAll(p, toExile);
try {
Thread.sleep(100); //delay for a tiny bit to give UI a chance catch up
} catch (InterruptedException e) {
e.printStackTrace();
}
p.drawCards(toExile.size() - 1);
toMulligan = new CardCollection(p.getCardsIn(ZoneType.Hand));
for (final Card c : toMulligan) {
moveToLibrary(c, null, null);
}
try {
Thread.sleep(100); //delay for a tiny bit to give UI a chance catch up
} catch (InterruptedException e) {
e.printStackTrace();
}
p.shuffle(null);
p.drawCards(handSize[i] - mulliganDelta);
p.onMulliganned();
allKept = false;
} else {
@@ -1726,21 +1761,17 @@ public class GameAction {
mulliganDelta++;
} while (!allKept);
if (isCommander) {
for (Entry<Player, Collection<Card>> kv : exiledDuringMulligans.entrySet()) {
Player p = kv.getKey();
Collection<Card> cc = kv.getValue();
for (Card c : cc) {
moveToLibrary(c, null, null);
}
p.shuffle(null);
//Vancouver Mulligan as a scry with the decisions inside
List<Player> scryers = Lists.newArrayList();
for(Player p : whoCanMulligan) {
if (p.getStartingHandSize() > p.getZone(ZoneType.Hand).size()) {
scryers.add(p);
}
}
//Vancouver Mulligan
for(Player p : whoCanMulligan) {
if (p.getStartingHandSize() > p.getZone(ZoneType.Hand).size()) {
p.scry(1, null);
for(Player p : scryers) {
if (p.getController().confirmMulliganScry(p)) {
scry(ImmutableList.of(p), 1, null);
}
}
}
@@ -1838,4 +1869,68 @@ public class GameAction {
runParams.put("Player", p);
game.getTriggerHandler().runTrigger(TriggerType.BecomeMonarch, runParams, false);
}
// Make scry an action function so that it can be used for mulligans (with a null cause)
// Assumes that the list of players is in APNAP order, which should be the case
// Optional here as well to handle the way that mulligans do the choice
// 701.17. Scry
// 701.17a To "scry N" means to look at the top N cards of your library, then put any number of them
// on the bottom of your library in any order and the rest on top of your library in any order.
// 701.17b If a player is instructed to scry 0, no scry event occurs. Abilities that trigger whenever a
// player scries wont trigger.
// 701.17c If multiple players scry at once, each of those players looks at the top cards of their library
// at the same time. Those players decide in APNAP order (see rule 101.4) where to put those
// cards, then those cards move at the same time.
public void scry(List<Player> players, int numScry, SpellAbility cause) {
if (numScry == 0) {
return;
}
// reveal the top N library cards to the player (only)
// no real need to separate out the look if
// there is only one player scrying
if (players.size() > 1) {
for (final Player p : players) {
final CardCollection topN = new CardCollection(p.getCardsIn(ZoneType.Library, numScry));
revealTo(topN, p);
}
}
// make the decisions
List<ImmutablePair<CardCollection, CardCollection>> decisions = Lists.newArrayList();
for (final Player p : players) {
final CardCollection topN = new CardCollection(p.getCardsIn(ZoneType.Library, numScry));
ImmutablePair<CardCollection, CardCollection> decision = p.getController().arrangeForScry(topN);
decisions.add(decision);
int numToTop = decision.getLeft() == null ? 0 : decision.getLeft().size();
int numToBottom = decision.getRight() == null ? 0 : decision.getRight().size();
// publicize the decision
game.fireEvent(new GameEventScry(p, numToTop, numToBottom));
}
// do the moves after all the decisions (maybe not necesssary, but let's
// do it the official way)
for (int i = 0; i < players.size(); i++) {
// no good iterate simultaneously in Java
final Player p = players.get(i);
final CardCollection toTop = decisions.get(i).getLeft();
final CardCollection toBottom = decisions.get(i).getRight();
if (toTop != null) {
Collections.reverse(toTop); // reverse to get the correct order
for (Card c : toTop) {
moveToLibrary(c, cause, null);
}
}
if (toBottom != null) {
for (Card c : toBottom) {
moveToBottomOfLibrary(c, cause, null);
}
}
if (cause != null) {
// set up triggers (but not actually do them until later)
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", p);
game.getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false);
}
}
}
}

View File

@@ -89,6 +89,8 @@ public final class GameActionUtil {
}
if (lkicheck) {
// double freeze tracker, so it doesn't update view
game.getTracker().freeze();
CardCollection preList = new CardCollection(source);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
}
@@ -164,6 +166,10 @@ public final class GameActionUtil {
// reset static abilities
if (lkicheck) {
game.getAction().checkStaticAbilities(false);
// clear delayed changes, this check should not have updated the view
game.getTracker().clearDelayed();
// need to unfreeze tracker
game.getTracker().unfreeze();
}
}

View File

@@ -28,6 +28,8 @@ import forge.game.cost.Cost;
import forge.game.spellability.*;
import forge.game.zone.ZoneType;
import forge.util.FileSection;
import io.sentry.Sentry;
import io.sentry.event.BreadcrumbBuilder;
import java.util.List;
import java.util.Map;
@@ -130,7 +132,16 @@ public final class AbilityFactory {
String source = state.getName().isEmpty() ? abString : state.getName();
throw new RuntimeException("AbilityFactory : getAbility -- no API in " + source + ": " + abString);
}
return getAbility(mapParams, type, state, parent);
try {
return getAbility(mapParams, type, state, parent);
} catch (Error | Exception ex) {
String msg = "AbilityFactory:getAbility: crash when trying to create ability ";
Sentry.getContext().recordBreadcrumb(
new BreadcrumbBuilder().setMessage(msg)
.withData("Card", state.getName()).withData("Ability", abString).build()
);
throw new RuntimeException(msg + " of card: " + state.getName(), ex);
}
}
public static final SpellAbility getAbility(final Card hostCard, final String svar) {

View File

@@ -225,10 +225,6 @@ public class AbilityUtils {
if (o != null && o instanceof Card) {
cards.add(game.getCardState((Card) o));
}
} else if (defined.equals("Clones")) {
for (final Card clone : hostCard.getClones()) {
cards.add(game.getCardState(clone));
}
} else if (defined.equals("Imprinted")) {
for (final Card imprint : hostCard.getImprintedCards()) {
cards.add(game.getCardState(imprint));

View File

@@ -173,8 +173,7 @@ public abstract class SpellAbilityEffect {
protected final static CardCollection getDefinedCardsOrTargeted(final SpellAbility sa, final String definedParam) { return getCards(true, definedParam, sa); }
private static CardCollection getCards(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam))
&& sa.getTargets() != null && (sa.getTargets().isTargetingAnyCard() || sa.getTargets().getTargets().isEmpty());
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
return useTargets ? new CardCollection(sa.getTargets().getTargetCards())
: AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam(definedParam), sa);
}
@@ -230,8 +229,17 @@ public abstract class SpellAbilityEffect {
if (desc.isEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append(location).append(" ");
if (location.equals("Hand")) {
sb.append("Return ");
} else if (location.equals("SacrificeCtrl")) {
sb.append("Its controller sacrifices ");
} else {
sb.append(location).append(" ");
}
sb.append(Lang.joinHomogenous(crds));
if (location.equals("Hand")) {
sb.append("to your hand").append(" ");
}
sb.append(" at the ");
if (combat) {
sb.append("end of combat.");
@@ -255,9 +263,18 @@ public abstract class SpellAbilityEffect {
final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), sa.getHostCard(), intrinsic);
for (final Card c : crds) {
trig.addRemembered(c);
// Svar for AI
if (!c.hasSVar("EndOfTurnLeavePlay")) {
c.setSVar("EndOfTurnLeavePlay", "AtEOT");
}
}
String trigSA = "";
if (location.equals("Sacrifice")) {
if (location.equals("Hand")) {
trigSA = "DB$ ChangeZone | Defined$ DelayTriggerRemembered | Origin$ Battlefield | Destination$ Hand";
} else if (location.equals("SacrificeCtrl")) {
trigSA = "DB$ SacrificeAll | Defined$ DelayTriggerRemembered";
} else if (location.equals("Sacrifice")) {
trigSA = "DB$ SacrificeAll | Defined$ DelayTriggerRemembered | Controller$ You";
} else if (location.equals("Exile")) {
trigSA = "DB$ ChangeZone | Defined$ DelayTriggerRemembered | Origin$ Battlefield | Destination$ Exile";
@@ -289,6 +306,11 @@ public abstract class SpellAbilityEffect {
}
trig.setOverridingAbility(AbilityFactory.getAbility(trigSA, card));
card.addTrigger(trig);
// Svar for AI
if (!card.hasSVar("EndOfTurnLeavePlay")) {
card.setSVar("EndOfTurnLeavePlay", "AtEOT");
}
}
protected static void addForgetOnMovedTrigger(final Card card, final String zone) {

View File

@@ -10,9 +10,11 @@ import forge.game.trigger.TriggerType;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
public class BecomesBlockedEffect extends SpellAbilityEffect {
@@ -38,10 +40,11 @@ public class BecomesBlockedEffect extends SpellAbilityEffect {
game.getCombat().setBlocked(c, true);
if (!c.getDamageHistory().getCreatureGotBlockedThisCombat()) {
isCombatChanged = true;
final HashMap<String, Object> runParams = new HashMap<String, Object>();
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Attacker", c);
runParams.put("Blockers", new ArrayList<Card>());
runParams.put("Blockers", Lists.<Card>newArrayList());
runParams.put("NumBlockers", 0);
runParams.put("Defender", game.getCombat().getDefenderByAttacker(c));
runParams.put("DefendingPlayer", game.getCombat().getDefenderPlayerByAttacker(c));
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false);
}

View File

@@ -79,7 +79,7 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
newTargetBlock.remove(oldTarget);
replaceIn.updateTarget(newTargetBlock);
// 3. test if updated choices would be correct.
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa), null);
GameObject newTarget = Iterables.getFirst(getDefinedCardsOrTargeted(sa, "DefinedMagnet"), null);
if (replaceIn.getSpellAbility(true).canTarget(newTarget)) {
newTargetBlock.add(newTarget);

View File

@@ -377,8 +377,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
* a {@link forge.game.spellability.SpellAbility} object.
*/
private void changeKnownOriginResolve(final SpellAbility sa) {
final boolean onlySpells = sa.hasParam("OnlySpells");
Iterable<Card> tgtCards = !onlySpells ? getTargetCards(sa) : new CardCollection();
Iterable<Card> tgtCards = getTargetCards(sa);
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Player player = sa.getActivatingPlayer();
final Card hostCard = sa.getHostCard();
@@ -400,7 +399,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
altDest = true;
}
}
final CardZoneTable triggerList = new CardZoneTable();
// changing zones for spells on the stack
for (final SpellAbility tgtSA : getTargetSpells(sa)) {
if (!tgtSA.isSpell()) { // Catch any abilities or triggers that slip through somehow
@@ -412,7 +412,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
continue;
}
removeFromStack(tgtSA, sa, si, game);
removeFromStack(tgtSA, sa, si, game, triggerList);
} // End of change from stack
final String remember = sa.getParam("RememberChanged");
@@ -429,7 +429,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
final boolean optional = sa.hasParam("Optional");
final long ts = game.getNextTimestamp();
final CardZoneTable triggerList = new CardZoneTable();
for (final Card tgtC : tgtCards) {
if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) {
@@ -606,6 +605,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (remember != null) {
hostCard.addRemembered(movedCard);
// addRememberedFromCardState ?
if (tgtC.getMeldedWith() != null) {
Card meld = game.getCardState(tgtC.getMeldedWith(), null);
if (meld != null) {
hostCard.addRemembered(meld);
}
}
}
if (forget != null) {
hostCard.removeRemembered(movedCard);
@@ -852,7 +858,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
// ensure that selection is within maximum allowed changeNum
do {
selectedCards = decider.getController().chooseCardsForZoneChange(destination, origin, sa, fetchList, delayedReveal, selectPrompt, decider);
selectedCards = decider.getController().chooseCardsForZoneChange(destination, origin, sa, fetchList, 0, changeNum, delayedReveal, selectPrompt, decider);
} while (selectedCards != null && selectedCards.size() > changeNum);
if (selectedCards != null) {
for (Card card : selectedCards) {
@@ -1121,6 +1127,13 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (remember) {
source.addRemembered(movedCard);
// addRememberedFromCardState ?
if (c.getMeldedWith() != null) {
Card meld = game.getCardState(c.getMeldedWith(), null);
if (meld != null) {
source.addRemembered(meld);
}
}
}
if (forget) {
source.removeRemembered(movedCard);
@@ -1169,36 +1182,39 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
* object.
* @param game
*/
private static void removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, final Game game) {
private static void removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, final Game game, CardZoneTable triggerList) {
final Card tgtHost = tgtSA.getHostCard();
final Zone originZone = tgtHost.getZone();
game.getStack().remove(si);
Map<String,Object> params = Maps.newHashMap();
params.put("StackSa", tgtSA);
params.put("StackSi", si);
Card movedCard = null;
if (srcSA.hasParam("Destination")) {
final boolean remember = srcSA.hasParam("RememberChanged");
if (tgtSA.isAbility()) {
// Shouldn't be able to target Abilities but leaving this in for now
} else if (srcSA.getParam("Destination").equals("Graveyard")) {
game.getAction().moveToGraveyard(tgtSA.getHostCard(), srcSA, params);
movedCard = game.getAction().moveToGraveyard(tgtHost, srcSA, params);
} else if (srcSA.getParam("Destination").equals("Exile")) {
Card host = srcSA.getOriginalHost();
if (host == null) {
host = srcSA.getHostCard();
}
tgtSA.getHostCard().setExiledWith(host);
game.getAction().exile(tgtSA.getHostCard(), srcSA, params);
movedCard = game.getAction().exile(tgtHost, srcSA, params);
movedCard.setExiledWith(host);
} else if (srcSA.getParam("Destination").equals("TopOfLibrary")) {
game.getAction().moveToLibrary(tgtSA.getHostCard(), srcSA, params);
movedCard = game.getAction().moveToLibrary(tgtHost, srcSA, params);
} else if (srcSA.getParam("Destination").equals("Hand")) {
game.getAction().moveToHand(tgtSA.getHostCard(), srcSA, params);
movedCard = game.getAction().moveToHand(tgtHost, srcSA, params);
} else if (srcSA.getParam("Destination").equals("BottomOfLibrary")) {
game.getAction().moveToBottomOfLibrary(tgtSA.getHostCard(), srcSA, params);
movedCard = game.getAction().moveToBottomOfLibrary(tgtHost, srcSA, params);
} else if (srcSA.getParam("Destination").equals("Library")) {
game.getAction().moveToBottomOfLibrary(tgtSA.getHostCard(), srcSA, params);
movedCard = game.getAction().moveToBottomOfLibrary(tgtHost, srcSA, params);
if (srcSA.hasParam("Shuffle") && "True".equals(srcSA.getParam("Shuffle"))) {
tgtSA.getHostCard().getOwner().shuffle(srcSA);
tgtHost.getOwner().shuffle(srcSA);
}
} else {
throw new IllegalArgumentException("AbilityFactory_ChangeZone: Invalid Destination argument for card "
@@ -1206,12 +1222,16 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
if (remember) {
srcSA.getHostCard().addRemembered(tgtSA.getHostCard());
srcSA.getHostCard().addRemembered(tgtHost);
// TODO or remember moved?
}
if (!tgtSA.isAbility()) {
System.out.println("Moving spell to " + srcSA.getParam("Destination"));
}
if (originZone != null && movedCard != null) {
triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard);
}
}
}
}

View File

@@ -67,6 +67,8 @@ public class CharmEffect extends SpellAbilityEffect {
if (num == min) {
sb.append(Lang.getNumeral(num));
} else if (min == 0) {
sb.append("up to ").append(Lang.getNumeral(num));
} else {
sb.append(Lang.getNumeral(min)).append(" or ").append(list.size() == 2 ? "both" : "more");
}
@@ -101,6 +103,8 @@ public class CharmEffect extends SpellAbilityEffect {
if (num == min) {
sb.append(Lang.getNumeral(num));
} else if (min == 0) {
sb.append("up to ").append(Lang.getNumeral(num));
} else {
sb.append(Lang.getNumeral(min)).append(" or ").append(list.size() == 2 ? "both" : "more");
}
@@ -146,19 +150,19 @@ public class CharmEffect extends SpellAbilityEffect {
return "";
}
public static void makeChoices(SpellAbility sa) {
public static boolean makeChoices(SpellAbility sa) {
//this resets all previous choices
sa.setSubAbility(null);
// Entwine does use all Choices
if (sa.isEntwine()) {
chainAbilities(sa, makePossibleOptions(sa));
return;
return true;
}
final int num = sa.hasParam("CharmNumOnResolve") ?
AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CharmNumOnResolve"), sa)
: Integer.parseInt(sa.hasParam("CharmNum") ? sa.getParam("CharmNum") : "1");
: Integer.parseInt(sa.getParamOrDefault("CharmNum", "1"));
final int min = sa.hasParam("MinCharmNum") ? Integer.parseInt(sa.getParam("MinCharmNum")) : num;
Card source = sa.getHostCard();
@@ -177,6 +181,7 @@ public class CharmEffect extends SpellAbilityEffect {
List<AbilitySub> chosen = chooser.getController().chooseModeForAbility(sa, min, num, sa.hasParam("CanRepeatModes"));
chainAbilities(sa, chosen);
return chosen != null && !chosen.isEmpty();
}
private static void chainAbilities(SpellAbility sa, List<AbilitySub> chosen) {

View File

@@ -1,6 +1,7 @@
package forge.game.ability.effects;
import forge.game.GameAction;
import forge.game.GameLogEntryType;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -59,9 +60,7 @@ public class ClashEffect extends SpellAbilityEffect {
* <p>
* clashWithOpponent.
* </p>
*
* @param source
* a {@link forge.game.card.Card} object.
*
* @return a boolean.
*/
private static boolean clashWithOpponent(final SpellAbility sa) {
@@ -126,11 +125,14 @@ public class ClashEffect extends SpellAbilityEffect {
private static void clashMoveToTopOrBottom(final Player p, final Card c, final SpellAbility sa) {
final GameAction action = p.getGame().getAction();
final boolean putOnTop = p.getController().willPutCardOnTop(c);
final String location = putOnTop ? "top" : "bottom";
final String clashOutcome = p.getName() + " clashed and put " + c.getName() + " to the " + location + " of library.";
if (putOnTop) {
action.moveToLibrary(c, sa);
} else {
action.moveToBottomOfLibrary(c, sa);
}
// computer just puts the card back until such time it can make a smarter decision
p.getGame().getGameLog().add(GameLogEntryType.STACK_RESOLVE, clashOutcome);
}
}

View File

@@ -202,7 +202,7 @@ public class CloneEffect extends SpellAbilityEffect {
tgtCard.clearImprintedCards();
// check if clone is now an Aura that needs to be attached
if (tgtCard.isAura()) {
if (tgtCard.isAura() && !tgtCard.getZone().is(ZoneType.Battlefield)) {
AttachEffect.attachAuraOnIndirectEnterBattlefield(tgtCard);
}

View File

@@ -164,7 +164,6 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
} else {
tgtCards = getTargetCards(sa);
}
host.clearClones();
for (final Card c : tgtCards) {
if (!sa.usesTargeting() || c.canBeTargetedBy(sa)) {
@@ -184,7 +183,6 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
//copyInPlay.setSetCode(c.getSetCode());
copyInPlay.setCloneOrigin(host);
sa.getHostCard().addClone(copyInPlay);
if (!pumpKeywords.isEmpty()) {
copyInPlay.addChangedCardKeywords(pumpKeywords, Lists.<String>newArrayList(), false, false, timestamp);
}
@@ -266,7 +264,6 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
final List<String> svars = Lists.newArrayList();
final List<String> triggers = Lists.newArrayList();
boolean asNonLegendary = false;
boolean resetActivations = false;
if (sa.hasParam("Keywords")) {
keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & ")));
@@ -277,9 +274,6 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
if (sa.hasParam("NonLegendary")) {
asNonLegendary = true;
}
if (sa.hasParam("ResetAbilityActivations")) {
resetActivations = true;
}
if (sa.hasParam("AddSVars")) {
svars.addAll(Arrays.asList(sa.getParam("AddSVars").split(" & ")));
}
@@ -411,11 +405,6 @@ public class CopyPermanentEffect extends SpellAbilityEffect {
copy.removeIntrinsicKeyword("Devoid");
}
if (resetActivations) {
for (SpellAbility ab : copy.getSpellAbilities()) {
ab.getRestrictions().resetTurnActivations();
}
}
copy.updateStateForView();
return copy;
}

View File

@@ -199,6 +199,14 @@ public class CountersPutEffect extends SpellAbilityEffect {
counterAmount = pc.chooseNumber(sa, "How many counters?", 0, counterAmount, params);
}
// Adapt need extra logic
if (sa.hasParam("Adapt")) {
if (!(tgtCard.getCounters(CounterType.P1P1) == 0
|| tgtCard.hasKeyword("CARDNAME adapts as though it had no +1/+1 counters"))) {
continue;
}
}
if (sa.hasParam("Tribute")) {
// make a copy to check if it would be on the battlefield
Card noTributeLKI = CardUtil.getLKICopy(tgtCard);
@@ -266,6 +274,13 @@ public class CountersPutEffect extends SpellAbilityEffect {
runParams.put("Card", tgtCard);
game.getTriggerHandler().runTrigger(TriggerType.BecomeRenowned, runParams, false);
}
if (sa.hasParam("Adapt")) {
// need to remove special keyword
tgtCard.removeHiddenExtrinsicKeyword("CARDNAME adapts as though it had no +1/+1 counters");
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", tgtCard);
game.getTriggerHandler().runTrigger(TriggerType.Adapt, runParams, false);
}
} else {
// adding counters to something like re-suspend cards
// etbcounter should apply multiplier

View File

@@ -4,6 +4,8 @@ import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.player.PlayerController;
@@ -69,11 +71,14 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
final Card card = sa.getHostCard();
final Game game = card.getGame();
final Player player = sa.getActivatingPlayer();
PlayerController pc = player.getController();
final String type = sa.getParam("CounterType");
final String num = sa.getParam("CounterNum");
int cntToRemove = 0;
if (!num.equals("All") && !num.equals("Remembered")) {
if (!num.equals("All") && !num.equals("Any") && !num.equals("Remembered")) {
cntToRemove = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);
}
@@ -96,6 +101,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
}
boolean rememberRemoved = sa.hasParam("RememberRemoved");
boolean rememberAmount = sa.hasParam("RememberAmount");
for (final Player tgtPlayer : getTargetPlayers(sa)) {
// Removing energy
@@ -107,7 +113,23 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
}
}
for (final Card tgtCard : getTargetCards(sa)) {
CardCollectionView srcCards = null;
if (sa.hasParam("ValidSource")) {
srcCards = game.getCardsIn(ZoneType.Battlefield);
srcCards = CardLists.getValidCards(srcCards, sa.getParam("ValidSource"), player, card, sa);
if (num.equals("Any")) {
StringBuilder sb = new StringBuilder();
sb.append("Choose cards to take ").append(counterType.getName()).append(" counters from");
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, sb.toString(), 0, srcCards.size(), true);
}
} else {
srcCards = getTargetCards(sa);
}
int totalRemoved = 0;
for (final Card tgtCard : srcCards) {
Card gameCard = game.getCardState(tgtCard, null);
// gameCard is LKI in that case, the card is not in game anymore
// or the timestamp did change
@@ -123,14 +145,12 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
}
game.updateLastStateForCard(gameCard);
continue;
} else if (num.equals("All")) {
} else if (num.equals("All") || num.equals("Any")) {
cntToRemove = gameCard.getCounters(counterType);
} else if (sa.getParam("CounterNum").equals("Remembered")) {
} else if (num.equals("Remembered")) {
cntToRemove = gameCard.getCountersAddedBy(card, counterType);
}
PlayerController pc = sa.getActivatingPlayer().getController();
if (type.equals("Any")) {
while (cntToRemove > 0 && gameCard.hasCounters()) {
final Map<CounterType, Integer> tgtCounters = gameCard.getCounters();
@@ -162,7 +182,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
cntToRemove = Math.min(cntToRemove, gameCard.getCounters(counterType));
if (zone.is(ZoneType.Battlefield) || zone.is(ZoneType.Exile)) {
if (sa.hasParam("UpTo")) {
if (sa.hasParam("UpTo") || num.equals("Any")) {
Map<String, Object> params = Maps.newHashMap();
params.put("Target", gameCard);
params.put("CounterType", type);
@@ -179,10 +199,17 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
}
}
game.updateLastStateForCard(gameCard);
totalRemoved += cntToRemove;
}
}
}
}
if (totalRemoved > 0 && rememberAmount) {
// TODO use SpellAbility Remember later
card.addRemembered(Integer.valueOf(totalRemoved));
}
}
}

View File

@@ -121,6 +121,9 @@ public class DamageAllEffect extends DamageBaseEffect {
if (!usedDamageMap) {
preventMap.triggerPreventDamage(false);
damageMap.triggerDamageDoneOnce(false, sa);
preventMap.clear();
damageMap.clear();
}
replaceDying(sa);

View File

@@ -30,7 +30,7 @@ public class DamageDealEffect extends DamageBaseEffect {
final int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
List<GameObject> tgts = getTargets(sa);
if (tgts.isEmpty())
if (tgts.isEmpty())
return "";
final List<Card> definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DamageSource"), sa);
@@ -131,15 +131,15 @@ public class DamageDealEffect extends DamageBaseEffect {
sa.setPreventMap(preventMap);
usedDamageMap = true;
}
final List<Card> definedSources = AbilityUtils.getDefinedCards(hostCard, sa.getParam("DamageSource"), sa);
if (definedSources == null || definedSources.isEmpty()) {
return;
}
for (Card source : definedSources) {
final Card sourceLKI = hostCard.getGame().getChangeZoneLKIInfo(source);
if (divideOnResolution) {
// Dividing Damage up to multiple targets using combat damage box
// Currently only used for Master of the Wild Hunt
@@ -147,7 +147,7 @@ public class DamageDealEffect extends DamageBaseEffect {
if (players.isEmpty()) {
return;
}
CardCollection assigneeCards = new CardCollection();
// Do we have a way of doing this in a better fashion?
for (GameObject obj : tgts) {
@@ -155,7 +155,7 @@ public class DamageDealEffect extends DamageBaseEffect {
assigneeCards.add((Card)obj);
}
}
Player assigningPlayer = players.get(0);
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true);
for (Entry<Card, Integer> dt : map.entrySet()) {
@@ -166,6 +166,9 @@ public class DamageDealEffect extends DamageBaseEffect {
preventMap.triggerPreventDamage(false);
// non combat damage cause lifegain there
damageMap.triggerDamageDoneOnce(false, sa);
preventMap.clear();
damageMap.clear();
}
replaceDying(sa);
return;
@@ -201,7 +204,7 @@ public class DamageDealEffect extends DamageBaseEffect {
}
}
}
if (remember) {
source.addRemembered(damageMap.row(sourceLKI).keySet());
}
@@ -210,6 +213,9 @@ public class DamageDealEffect extends DamageBaseEffect {
preventMap.triggerPreventDamage(false);
// non combat damage cause lifegain there
damageMap.triggerDamageDoneOnce(false, sa);
preventMap.clear();
damageMap.clear();
}
replaceDying(sa);
}

View File

@@ -132,6 +132,9 @@ public class DamageEachEffect extends DamageBaseEffect {
if (!usedDamageMap) {
preventMap.triggerPreventDamage(false);
damageMap.triggerDamageDoneOnce(false, sa);
preventMap.clear();
damageMap.clear();
}
replaceDying(sa);

View File

@@ -21,10 +21,12 @@ public class DamageResolveEffect extends SpellAbilityEffect {
if (preventMap != null) {
preventMap.triggerPreventDamage(false);
preventMap.clear();
}
// non combat damage cause lifegain there
if (damageMap != null) {
damageMap.triggerDamageDoneOnce(false, sa);
damageMap.clear();
}
}

View File

@@ -187,14 +187,14 @@ public class DigEffect extends SpellAbilityEffect {
if (!andOrValid.equals("")) {
andOrCards = CardLists.getValidCards(top, andOrValid.split(","), host.getController(), host, sa);
andOrCards.removeAll((Collection<?>)valid);
valid.addAll(andOrCards);
valid.addAll(andOrCards); //pfps need to add andOr cards to valid to have set of all valid cards set up
}
else {
andOrCards = new CardCollection();
}
}
else {
// If all the cards are valid choices, no need for a separate reveal dialog to the chooser.
// If all the cards are valid choices, no need for a separate reveal dialog to the chooser. pfps??
if (p == chooser && destZone1ChangeNum > 1) {
delayedReveal = null;
}
@@ -238,54 +238,42 @@ public class DigEffect extends SpellAbilityEffect {
if (sa.hasParam("RandomOrder")) {
CardLists.shuffle(movedCards);
}
}
else {
} else {
String prompt;
if (sa.hasParam("PrimaryPrompt")) {
prompt = sa.getParam("PrimaryPrompt");
} else {
prompt = "Choose a card to put into " + destZone1.name();
prompt = "Choose card(s) to put into " + destZone1.name();
if (destZone1.equals(ZoneType.Library)) {
if (libraryPosition == -1) {
prompt = "Choose a card to put on the bottom of {player's} library";
}
else if (libraryPosition == 0) {
prompt = "Choose a card to put on top of {player's} library";
prompt = "Choose card(s) to put on the bottom of {player's} library";
} else if (libraryPosition == 0) {
prompt = "Choose card(s) to put on top of {player's} library";
}
}
}
movedCards = new CardCollection();
for (int i = 0; i < destZone1ChangeNum || (anyNumber && i < numToDig); i++) {
// let user get choice
Card chosen = null;
if (!valid.isEmpty()) {
// If we're choosing multiple cards, only need to show the reveal dialog the first time through.
boolean shouldReveal = (i == 0);
chosen = chooser.getController().chooseSingleEntityForEffect(valid, shouldReveal ? delayedReveal : null, sa, prompt, anyNumber || optional, p);
if (valid.isEmpty()) {
chooser.getController().notifyOfValue(sa, null, "No valid cards");
} else {
if ( p == chooser ) { // the digger can still see all the dug cards when choosing
chooser.getController().tempShowCards(top);
}
else {
if (i == 0) {
chooser.getController().notifyOfValue(sa, null, "No valid cards");
}
}
if (chosen == null) {
break;
}
movedCards.add(chosen);
valid.remove(chosen);
List<Card> chosen = new ArrayList<Card>();
if (!andOrValid.equals("")) {
andOrCards.remove(chosen);
if (!chosen.isValid(andOrValid.split(","), host.getController(), host, sa)) {
valid = new CardCollection(andOrCards);
}
else if (!chosen.isValid(changeValid.split(","), host.getController(), host, sa)) {
valid.removeAll((Collection<?>)andOrCards);
valid.removeAll(andOrCards); //pfps remove andOr cards to get two two choices set up correctly
chosen = chooser.getController().chooseFromTwoListsForEffect(valid, andOrCards, optional, delayedReveal, sa, prompt, p);
} else {
int max = anyNumber ? valid.size() : Math.min(valid.size(),destZone1ChangeNum);
int min = (anyNumber || optional) ? 0 : max;
if ( max > 0 ) { // if max is 0 don't make a choice
chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p);
}
}
chooser.getController().endTempShowCards();
movedCards.addAll(chosen);
}
if (!changeValid.isEmpty() && !sa.hasParam("ExileFaceDown") && !sa.hasParam("NoReveal")) {
@@ -350,8 +338,7 @@ public class DigEffect extends SpellAbilityEffect {
CardCollection afterOrder = rest;
if (sa.hasParam("RestRandomOrder")) {
CardLists.shuffle(afterOrder);
}
else if (!skipReorder && rest.size() > 1) {
} else if (!skipReorder && rest.size() > 1) {
if (destZone2 == ZoneType.Graveyard) {
afterOrder = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, rest, destZone2);
} else {

View File

@@ -153,6 +153,9 @@ public class FightEffect extends DamageBaseEffect {
if (!usedDamageMap) {
preventMap.triggerPreventDamage(false);
damageMap.triggerDamageDoneOnce(false, sa);
preventMap.clear();
damageMap.clear();
}
replaceDying(sa);

View File

@@ -54,9 +54,9 @@ public class MeldEffect extends SpellAbilityEffect {
}
primary.changeToState(CardStateName.Meld);
primary.setMeldedWith(secondary);
PlayerZoneBattlefield bf = (PlayerZoneBattlefield)controller.getZone(ZoneType.Battlefield);
Card melded = game.getAction().changeZone(primary.getZone(), bf, primary, 0, sa);
game.getAction().changeZone(primary.getZone(), bf, primary, 0, sa);
bf.addToMelded(secondary);
melded.setMeldedWith(secondary);
}
}

View File

@@ -72,6 +72,7 @@ public class PlayEffect extends SpellAbilityEffect {
final Player controller = activator;
CardCollection tgtCards;
CardCollection showCards = new CardCollection();
if (sa.hasParam("Valid")) {
ZoneType zone = ZoneType.Hand;
@@ -81,6 +82,9 @@ public class PlayEffect extends SpellAbilityEffect {
tgtCards = new CardCollection(
AbilityUtils.filterListByType(game.getCardsIn(zone), sa.getParam("Valid"), sa)
);
if ( sa.hasParam("ShowCards") ) {
showCards = new CardCollection(AbilityUtils.filterListByType(game.getCardsIn(zone), sa.getParam("ShowCards"), sa));
}
}
else if (sa.hasParam("AnySupportedCard")) {
List<PaperCard> cards = Lists.newArrayList(StaticData.instance().getCommonCards().getUniqueCards());
@@ -140,7 +144,9 @@ public class PlayEffect extends SpellAbilityEffect {
final CardCollection saidNoTo = new CardCollection();
while (tgtCards.size() > saidNoTo.size() && saidNoTo.size() < amount && amount > 0) {
activator.getController().tempShowCards(showCards);
Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, "Select a card to play");
activator.getController().endTempShowCards();
if (tgtCard == null) {
return;
}

View File

@@ -94,32 +94,13 @@ public class PumpEffect extends SpellAbilityEffect {
game.fireEvent(new GameEventCardStatsChanged(gameCard));
}
};
if (sa.hasParam("UntilEndOfCombat")) {
game.getEndOfCombat().addUntil(untilEOT);
} else if (sa.hasParam("UntilYourNextUpkeep")) {
game.getUpkeep().addUntil(sa.getActivatingPlayer(), untilEOT);
} else if (sa.hasParam("UntilHostLeavesPlay")) {
sa.getHostCard().addLeavesPlayCommand(untilEOT);
} else if (sa.hasParam("UntilHostLeavesPlayOrEOT")) {
sa.getHostCard().addLeavesPlayCommand(untilEOT);
game.getEndOfTurn().addUntil(untilEOT);
} else if (sa.hasParam("UntilLoseControlOfHost")) {
sa.getHostCard().addLeavesPlayCommand(untilEOT);
sa.getHostCard().addChangeControllerCommand(untilEOT);
} else if (sa.hasParam("UntilYourNextTurn")) {
game.getCleanup().addUntil(sa.getActivatingPlayer(), untilEOT);
} else if (sa.hasParam("UntilUntaps")) {
sa.getHostCard().addUntapCommand(untilEOT);
} else {
game.getEndOfTurn().addUntil(untilEOT);
}
addUntilCommand(sa, untilEOT);
}
game.fireEvent(new GameEventCardStatsChanged(gameCard));
}
private static void applyPump(final SpellAbility sa, final Player p,
final List<String> keywords, final long timestamp) {
final Game game = p.getGame();
final Card host = sa.getHostCard();
//if host is not on the battlefield don't apply
// Suspend should does Affect the Stack
@@ -144,16 +125,32 @@ public class PumpEffect extends SpellAbilityEffect {
}
}
};
if (sa.hasParam("UntilEndOfCombat")) {
game.getEndOfCombat().addUntil(untilEOT);
} else if (sa.hasParam("UntilYourNextUpkeep")) {
game.getUpkeep().addUntil(sa.getActivatingPlayer(), untilEOT);
} else if (sa.hasParam("UntilLoseControlOfHost")) {
sa.getHostCard().addLeavesPlayCommand(untilEOT);
sa.getHostCard().addChangeControllerCommand(untilEOT);
} else {
game.getEndOfTurn().addUntil(untilEOT);
}
addUntilCommand(sa, untilEOT);
}
}
private static void addUntilCommand(final SpellAbility sa, GameCommand untilEOT) {
final Card host = sa.getHostCard();
final Game game = host.getGame();
if (sa.hasParam("UntilEndOfCombat")) {
game.getEndOfCombat().addUntil(untilEOT);
} else if (sa.hasParam("UntilYourNextUpkeep")) {
game.getUpkeep().addUntil(sa.getActivatingPlayer(), untilEOT);
} else if (sa.hasParam("UntilHostLeavesPlay")) {
host.addLeavesPlayCommand(untilEOT);
} else if (sa.hasParam("UntilHostLeavesPlayOrEOT")) {
host.addLeavesPlayCommand(untilEOT);
game.getEndOfTurn().addUntil(untilEOT);
} else if (sa.hasParam("UntilLoseControlOfHost")) {
host.addLeavesPlayCommand(untilEOT);
host.addChangeControllerCommand(untilEOT);
} else if (sa.hasParam("UntilYourNextTurn")) {
game.getCleanup().addUntil(sa.getActivatingPlayer(), untilEOT);
} else if (sa.hasParam("UntilUntaps")) {
host.addUntapCommand(untilEOT);
} else {
game.getEndOfTurn().addUntil(untilEOT);
}
}

View File

@@ -4,17 +4,17 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import java.util.List;
import com.google.common.collect.Lists;
public class ScryEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
final StringBuilder sb = new StringBuilder();
final List<Player> tgtPlayers = getTargetPlayers(sa);
for (final Player p : tgtPlayers) {
for (final Player p : getTargetPlayers(sa)) {
sb.append(p.toString()).append(" ");
}
@@ -36,19 +36,16 @@ public class ScryEffect extends SpellAbilityEffect {
boolean isOptional = sa.hasParam("Optional");
final TargetRestrictions tgt = sa.getTargetRestrictions();
final List<Player> tgtPlayers = getTargetPlayers(sa);
final List<Player> players = Lists.newArrayList(); // players really affected
for (final Player p : tgtPlayers) {
if ((tgt == null) || p.canBeTargetedBy(sa)) {
if (isOptional && !p.getController().confirmAction(sa, null, "Do you want to scry?")) {
continue;
}
p.scry(num, sa);
}
}
// Optional here for spells that have optional multi-player scrying
for (final Player p : getTargetPlayers(sa)) {
if ( (!sa.usesTargeting() || p.canBeTargetedBy(sa)) &&
(!isOptional || p.getController().confirmAction(sa, null, "Do you want to scry?")) ) {
players.add(p);
}
}
sa.getActivatingPlayer().getGame().getAction().scry(players, num, sa);
}
}

View File

@@ -493,21 +493,27 @@ public class TokenEffect extends SpellAbilityEffect {
lki.setLastKnownZone(tok.getController().getZone(ZoneType.Battlefield));
// double freeze tracker, so it doesn't update view
game.getTracker().freeze();
CardCollection preList = new CardCollection(lki);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(lki), preList);
// TODO update when doing Attach Update
boolean canAttach = lki.isAttachment();
if (canAttach && ge.canBeAttached(lki)) {
if (canAttach && !ge.canBeAttached(lki)) {
canAttach = false;
}
// reset static abilities
game.getAction().checkStaticAbilities(false);
// clear delayed changes, this check should not have updated the view
game.getTracker().clearDelayed();
// need to unfreeze tracker
game.getTracker().unfreeze();
if (!canAttach) {
// Token can't attach it
// Token can't attach to it
return false;
}

View File

@@ -101,7 +101,7 @@ public class Card extends GameEntity implements Comparable<Card> {
// cards attached or otherwise linked to this card
private CardCollection hauntedBy, devouredCards, delvedCards, convokedCards, imprintedCards, encodedCards;
private CardCollection mustBlockCards, clones, gainControlTargets, chosenCards, blockedThisTurn, blockedByThisTurn;
private CardCollection mustBlockCards, gainControlTargets, chosenCards, blockedThisTurn, blockedByThisTurn;
// if this card is attached or linked to something, what card is it currently attached to
private Card encoding, cloneOrigin, haunting, effectSource, pairedWith, meldedWith;
@@ -528,30 +528,30 @@ public class Card extends GameEntity implements Comparable<Card> {
public Card manifest(Player p, SpellAbility sa) {
// Turn Face Down (even if it's DFC).
CardState originalCard = this.getState(CardStateName.Original);
ManaCost cost = originalCard.getManaCost();
ManaCost cost = getState(CardStateName.Original).getManaCost();
boolean isCreature = this.isCreature();
boolean isCreature = isCreature();
// Sometimes cards are manifested while already being face down
if (!turnFaceDown(true) && currentStateName != CardStateName.FaceDown) {
return null;
}
// Sometimes cards are manifested while already being face down
if (!turnFaceDown(true) && !isFaceDown()) {
return null;
}
// Move to p's battlefield
Game game = p.getGame();
// Just in case you aren't the controller, now you are!
this.setController(p, game.getNextTimestamp());
// Just in case you aren't the controller, now you are!
setController(p, game.getNextTimestamp());
// Mark this card as "manifested"
this.setPreFaceDownState(CardStateName.Original);
this.setManifested(true);
setPreFaceDownState(CardStateName.Original);
setManifested(true);
Card c = game.getAction().moveToPlay(this, p, sa);
// Add manifest demorph static ability for creatures
if (isCreature && !cost.isNoCost()) {
c.addSpellAbility(CardFactoryUtil.abilityManifestFaceUp(c, cost));
// Add Manifest to original State
c.getState(CardStateName.Original).addSpellAbility(CardFactoryUtil.abilityManifestFaceUp(c, cost));
c.updateStateForView();
}
@@ -1031,22 +1031,6 @@ public class Card extends GameEntity implements Comparable<Card> {
public final GameEntity getMustAttackEntityThisTurn() { return mustAttackEntityThisTurn; }
public final void setMustAttackEntityThisTurn(GameEntity entThisTurn) { mustAttackEntityThisTurn = entThisTurn; }
public final CardCollectionView getClones() {
return CardCollection.getView(clones);
}
public final void setClones(final Iterable<Card> clones0) {
clones = clones0 == null ? null : new CardCollection(clones0);
}
public final void addClone(final Card c) {
if (clones == null) {
clones = new CardCollection();
}
clones.add(c);
}
public final void clearClones() {
clones = null;
}
public final Card getCloneOrigin() {
return cloneOrigin;
}
@@ -1067,7 +1051,8 @@ public class Card extends GameEntity implements Comparable<Card> {
}
public final boolean hasConverge() {
return "Count$Converge".equals(getSVar("X")) || "Count$Converge".equals(getSVar("Y")) || hasKeyword("Sunburst");
return "Count$Converge".equals(getSVar("X")) || "Count$Converge".equals(getSVar("Y")) ||
hasKeyword(Keyword.SUNBURST) || hasKeyword("Modular:Sunburst");
}
@Override
@@ -1463,250 +1448,262 @@ public class Card extends GameEntity implements Comparable<Card> {
int i = 0;
for (KeywordInterface inst : keywords) {
String keyword = inst.getOriginal();
if (keyword.startsWith("SpellCantTarget")) {
continue;
}
// format text changes
if (CardUtil.isKeywordModifiable(keyword)
&& keywordsGrantedByTextChanges.contains(inst)) {
for (final Entry<String, String> e : textChanges) {
final String value = e.getValue();
if (keyword.contains(value)) {
keyword = TextUtil.fastReplace(keyword, value,
TextUtil.concatNoSpace("<strike>", e.getKey(), "</strike> ", value));
// assume (for now) max one change per keyword
break;
try {
if (keyword.startsWith("SpellCantTarget")) {
continue;
}
// format text changes
if (CardUtil.isKeywordModifiable(keyword)
&& keywordsGrantedByTextChanges.contains(inst)) {
for (final Entry<String, String> e : textChanges) {
final String value = e.getValue();
if (keyword.contains(value)) {
keyword = TextUtil.fastReplace(keyword, value,
TextUtil.concatNoSpace("<strike>", e.getKey(), "</strike> ", value));
// assume (for now) max one change per keyword
break;
}
}
}
}
if (keyword.startsWith("CantBeCounteredBy")) {
final String[] p = keyword.split(":");
sbLong.append(p[2]).append("\r\n");
} else if (keyword.startsWith("etbCounter")) {
final String[] p = keyword.split(":");
final StringBuilder s = new StringBuilder();
if (p.length > 4) {
if (!"no desc".equals(p[4])) {
s.append(p[4]);
if (keyword.startsWith("CantBeCounteredBy") || keyword.startsWith("Panharmonicon")
|| keyword.startsWith("Dieharmonicon")) {
final String[] p = keyword.split(":");
sbLong.append(p[2]).append("\r\n");
} else if (keyword.startsWith("etbCounter")) {
final String[] p = keyword.split(":");
final StringBuilder s = new StringBuilder();
if (p.length > 4) {
if (!"no desc".equals(p[4])) {
s.append(p[4]);
}
} else {
s.append(getName());
s.append(" enters the battlefield with ");
s.append(Lang.nounWithNumeral(p[2], CounterType.valueOf(p[1]).getName() + " counter"));
s.append(" on it.");
}
} else {
s.append(getName());
s.append(" enters the battlefield with ");
s.append(Lang.nounWithNumeral(p[2], CounterType.valueOf(p[1]).getName() + " counter"));
s.append(" on it.");
}
sbLong.append(s).append("\r\n");
} else if (keyword.startsWith("Protection:")) {
final String[] k = keyword.split(":");
sbLong.append(k[2]).append("\r\n");
} else if (keyword.startsWith("Creatures can't attack unless their controller pays")) {
final String[] k = keyword.split(":");
if (!k[3].equals("no text")) {
sbLong.append(k[3]).append("\r\n");
}
} else if (keyword.startsWith("Enchant")) {
String k = keyword;
k = TextUtil.fastReplace(k, "Curse", "");
sbLong.append(k).append("\r\n");
} else if (keyword.startsWith("Ripple")) {
sbLong.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n");
} else if (keyword.startsWith("Madness")) {
String[] parts = keyword.split(":");
// If no colon exists in Madness keyword, it must have been granted and assumed the cost from host
if (parts.length < 2) {
sbLong.append(parts[0]).append(" ").append(this.getManaCost()).append("\r\n");
} else {
sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n");
}
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")) {
String[] k = keyword.split(":");
sbLong.append(k[0]);
if (k.length > 1) {
final Cost mCost = new Cost(k[1], true);
if (!mCost.isOnlyManaCost()) {
sbLong.append("");
sbLong.append(s).append("\r\n");
} else if (keyword.startsWith("Protection:")) {
final String[] k = keyword.split(":");
sbLong.append(k[2]).append("\r\n");
} else if (keyword.startsWith("Creatures can't attack unless their controller pays")) {
final String[] k = keyword.split(":");
if (!k[3].equals("no text")) {
sbLong.append(k[3]).append("\r\n");
}
if (mCost.isOnlyManaCost()) {
sbLong.append(" ");
} else if (keyword.startsWith("Enchant")) {
String k = keyword;
k = TextUtil.fastReplace(k, "Curse", "");
sbLong.append(k).append("\r\n");
} else if (keyword.startsWith("Ripple")) {
sbLong.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n");
} else if (keyword.startsWith("Madness")) {
String[] parts = keyword.split(":");
// If no colon exists in Madness keyword, it must have been granted and assumed the cost from host
if (parts.length < 2) {
sbLong.append(parts[0]).append(" ").append(this.getManaCost()).append("\r\n");
} else {
sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n");
}
sbLong.append(mCost.toString()).delete(sbLong.length() - 2, sbLong.length());
if (!mCost.isOnlyManaCost()) {
sbLong.append(".");
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")) {
String[] k = keyword.split(":");
sbLong.append(k[0]);
if (k.length > 1) {
final Cost mCost = new Cost(k[1], true);
if (!mCost.isOnlyManaCost()) {
sbLong.append("");
}
if (mCost.isOnlyManaCost()) {
sbLong.append(" ");
}
sbLong.append(mCost.toString()).delete(sbLong.length() - 2, sbLong.length());
if (!mCost.isOnlyManaCost()) {
sbLong.append(".");
}
sbLong.append(" (" + inst.getReminderText() + ")");
sbLong.append("\r\n");
}
} else if (keyword.startsWith("Emerge")) {
final String[] k = keyword.split(":");
sbLong.append(k[0]).append(" ").append(ManaCostParser.parse(k[1]));
sbLong.append(" (" + inst.getReminderText() + ")");
sbLong.append("\r\n");
}
} else if (keyword.startsWith("Emerge")) {
final String[] k = keyword.split(":");
sbLong.append(k[0]).append(" ").append(ManaCostParser.parse(k[1]));
sbLong.append(" (" + inst.getReminderText() + ")");
sbLong.append("\r\n");
} else if (keyword.startsWith("Echo")) {
sbLong.append("Echo ");
final String[] upkeepCostParams = keyword.split(":");
sbLong.append(upkeepCostParams.length > 2 ? "- " + upkeepCostParams[2] : ManaCostParser.parse(upkeepCostParams[1]));
sbLong.append(" (At the beginning of your upkeep, if CARDNAME came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)");
sbLong.append("\r\n");
} else if (keyword.startsWith("Cumulative upkeep")) {
sbLong.append("Cumulative upkeep ");
final String[] upkeepCostParams = keyword.split(":");
sbLong.append(upkeepCostParams.length > 2 ? "- " + upkeepCostParams[2] : ManaCostParser.parse(upkeepCostParams[1]));
sbLong.append("\r\n");
} else if (keyword.startsWith("Alternative Cost")) {
sbLong.append("Has alternative cost.");
} else if (keyword.startsWith("AlternateAdditionalCost")) {
final String costString1 = keyword.split(":")[1];
final String costString2 = keyword.split(":")[2];
final Cost cost1 = new Cost(costString1, false);
final Cost cost2 = new Cost(costString2, false);
sbLong.append("As an additional cost to cast ")
.append(getName()).append(", ")
.append(cost1.toSimpleString())
.append(" or pay ")
.append(cost2.toSimpleString())
.append(".\r\n");
} else if (keyword.startsWith("Multikicker")) {
if (!keyword.endsWith("Generic")) {
final String[] n = keyword.split(":");
final Cost cost = new Cost(n[1], false);
sbLong.append("Multikicker ").append(cost.toSimpleString());
sbLong.append(" (" + inst.getReminderText() + ")").append("\r\n");
}
} else if (keyword.startsWith("Kicker")) {
if (!keyword.endsWith("Generic")) {
final StringBuilder sbx = new StringBuilder();
final String[] n = keyword.split(":");
sbx.append("Kicker ");
final Cost cost = new Cost(n[1], false);
sbx.append(cost.toSimpleString());
if (Lists.newArrayList(n).size() > 2) {
sbx.append(" and/or ");
final Cost cost2 = new Cost(n[2], false);
sbx.append(cost2.toSimpleString());
} else if (keyword.startsWith("Echo")) {
sbLong.append("Echo ");
final String[] upkeepCostParams = keyword.split(":");
sbLong.append(upkeepCostParams.length > 2 ? "- " + upkeepCostParams[2] : ManaCostParser.parse(upkeepCostParams[1]));
sbLong.append(" (At the beginning of your upkeep, if CARDNAME came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)");
sbLong.append("\r\n");
} else if (keyword.startsWith("Cumulative upkeep")) {
sbLong.append("Cumulative upkeep ");
final String[] upkeepCostParams = keyword.split(":");
sbLong.append(upkeepCostParams.length > 2 ? "- " + upkeepCostParams[2] : ManaCostParser.parse(upkeepCostParams[1]));
sbLong.append("\r\n");
} else if (keyword.startsWith("Alternative Cost")) {
sbLong.append("Has alternative cost.");
} else if (keyword.startsWith("AlternateAdditionalCost")) {
final String costString1 = keyword.split(":")[1];
final String costString2 = keyword.split(":")[2];
final Cost cost1 = new Cost(costString1, false);
final Cost cost2 = new Cost(costString2, false);
sbLong.append("As an additional cost to cast ")
.append(getName()).append(", ")
.append(cost1.toSimpleString())
.append(" or pay ")
.append(cost2.toSimpleString())
.append(".\r\n");
} else if (keyword.startsWith("Multikicker")) {
if (!keyword.endsWith("Generic")) {
final String[] n = keyword.split(":");
final Cost cost = new Cost(n[1], false);
sbLong.append("Multikicker ").append(cost.toSimpleString());
sbLong.append(" (" + inst.getReminderText() + ")").append("\r\n");
}
sbx.append(" (" + inst.getReminderText() + ")");
sbLong.append(sbx).append("\r\n");
}
} else if (keyword.startsWith("Hexproof:")) {
final String k[] = keyword.split(":");
sbLong.append("Hexproof from ").append(k[2])
.append(" (").append(inst.getReminderText()).append(")").append("\r\n");
} else if (keyword.endsWith(".") && !keyword.startsWith("Haunt")) {
sbLong.append(keyword).append("\r\n");
} else if (keyword.startsWith("Presence") || keyword.startsWith("MayFlash")) {
// Pseudo keywords, only print Reminder
sbLong.append(inst.getReminderText());
} else if (keyword.contains("At the beginning of your upkeep, ")
&& keyword.contains(" unless you pay")) {
sbLong.append(keyword).append("\r\n");
} else if (keyword.startsWith("Strive") || keyword.startsWith("Escalate")
|| keyword.startsWith("ETBReplacement")
|| keyword.startsWith("CantBeBlockedBy ")
|| keyword.startsWith("Affinity")
|| keyword.equals("CARDNAME enters the battlefield tapped.")
|| keyword.startsWith("UpkeepCost")) {
} else if (keyword.equals("Provoke") || keyword.equals("Ingest") || keyword.equals("Unleash")
|| keyword.equals("Soulbond") || keyword.equals("Partner") || keyword.equals("Retrace")
|| keyword.equals("Living Weapon") || keyword.equals("Myriad") || keyword.equals("Exploit")
|| keyword.equals("Changeling") || keyword.equals("Delve")
|| keyword.equals("Split second")
|| keyword.equals("Suspend") // for the ones without amounnt
|| keyword.equals("Hideaway") || keyword.equals("Ascend")
|| keyword.equals("Totem armor") || keyword.equals("Battle cry")
|| keyword.equals("Devoid") || keyword.equals("Riot")){
sbLong.append(keyword + " (" + inst.getReminderText() + ")");
} else if (keyword.startsWith("Partner:")) {
final String[] k = keyword.split(":");
sbLong.append("Partner with " + k[1] + " (" + inst.getReminderText() + ")");
} else if (keyword.startsWith("Modular") || keyword.startsWith("Bloodthirst") || keyword.startsWith("Dredge")
|| keyword.startsWith("Fabricate") || keyword.startsWith("Soulshift") || keyword.startsWith("Bushido")
|| keyword.startsWith("Crew") || keyword.startsWith("Tribute") || keyword.startsWith("Absorb")
|| keyword.startsWith("Graft") || keyword.startsWith("Fading") || keyword.startsWith("Vanishing")
|| keyword.startsWith("Afterlife")
|| keyword.startsWith("Afflict") || keyword.startsWith ("Poisonous") || keyword.startsWith("Rampage")
|| keyword.startsWith("Renown") || keyword.startsWith("Annihilator") || keyword.startsWith("Devour")) {
final String[] k = keyword.split(":");
sbLong.append(k[0] + " " + k[1] + " (" + inst.getReminderText() + ")");
} else if (keyword.contains("Haunt")) {
sb.append("\r\nHaunt (");
if (isCreature()) {
sb.append("When this creature dies, exile it haunting target creature.");
} else {
sb.append("When this spell card is put into a graveyard after resolving, ");
sb.append("exile it haunting target creature.");
}
sb.append(")");
} else if (keyword.equals("Convoke") || keyword.equals("Dethrone")|| keyword.equals("Fear")
|| keyword.equals("Melee") || keyword.equals("Improvise")|| keyword.equals("Shroud")
|| keyword.equals("Banding") || keyword.equals("Intimidate")|| keyword.equals("Evolve")
|| keyword.equals("Exalted") || keyword.equals("Extort")|| keyword.equals("Flanking")
|| keyword.equals("Horsemanship") || keyword.equals("Infect")|| keyword.equals("Persist")
|| keyword.equals("Phasing") || keyword.equals("Shadow")|| keyword.equals("Skulk")
|| keyword.equals("Undying") || keyword.equals("Wither") || keyword.equals("Cascade")
|| keyword.equals("Mentor")) {
if (sb.length() != 0) {
sb.append("\r\n");
}
sb.append(keyword + " (" + inst.getReminderText() + ")");
} else if (keyword.endsWith(" offering")) {
String offeringType = keyword.split(" ")[0];
if (sb.length() != 0) {
sb.append("\r\n");
}
sbLong.append(keyword);
sbLong.append(" (" + Keyword.getInstance("Offering:"+ offeringType).getReminderText() + ")");
} else if (keyword.startsWith("Equip") || keyword.startsWith("Fortify") || keyword.startsWith("Outlast")
|| keyword.startsWith("Unearth") || keyword.startsWith("Scavenge") || keyword.startsWith("Spectacle")
|| keyword.startsWith("Evoke") || keyword.startsWith("Bestow") || keyword.startsWith("Dash")
|| keyword.startsWith("Surge") || keyword.startsWith("Transmute") || keyword.startsWith("Suspend")
|| keyword.equals("Undaunted") || keyword.startsWith("Monstrosity") || keyword.startsWith("Embalm")
|| keyword.startsWith("Level up") || keyword.equals("Prowess") || keyword.startsWith("Eternalize")
|| keyword.startsWith("Reinforce") || keyword.startsWith("Champion") || keyword.startsWith("Prowl")
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Adapt")
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")) {
// keyword parsing takes care of adding a proper description
} else if (keyword.startsWith("CantBeBlockedByAmount")) {
sbLong.append(getName()).append(" can't be blocked ");
sbLong.append(getTextForKwCantBeBlockedByAmount(keyword));
} else if (keyword.startsWith("CantBlock")) {
sbLong.append(getName()).append(" can't block ");
if (keyword.contains("CardUID")) {
sbLong.append("CardID (").append(Integer.valueOf(keyword.split("CantBlockCardUID_")[1])).append(")");
} else {
} else if (keyword.startsWith("Kicker")) {
if (!keyword.endsWith("Generic")) {
final StringBuilder sbx = new StringBuilder();
final String[] n = keyword.split(":");
sbx.append("Kicker ");
final Cost cost = new Cost(n[1], false);
sbx.append(cost.toSimpleString());
if (Lists.newArrayList(n).size() > 2) {
sbx.append(" and/or ");
final Cost cost2 = new Cost(n[2], false);
sbx.append(cost2.toSimpleString());
}
sbx.append(" (" + inst.getReminderText() + ")");
sbLong.append(sbx).append("\r\n");
}
} else if (keyword.startsWith("Hexproof:")) {
final String k[] = keyword.split(":");
sbLong.append("Hexproof from ").append(k[2])
.append(" (").append(inst.getReminderText()).append(")").append("\r\n");
} else if (keyword.endsWith(".") && !keyword.startsWith("Haunt")) {
sbLong.append(keyword).append("\r\n");
} else if (keyword.startsWith("Presence") || keyword.startsWith("MayFlash")) {
// Pseudo keywords, only print Reminder
sbLong.append(inst.getReminderText());
} else if (keyword.contains("At the beginning of your upkeep, ")
&& keyword.contains(" unless you pay")) {
sbLong.append(keyword).append("\r\n");
} else if (keyword.startsWith("Strive") || keyword.startsWith("Escalate")
|| keyword.startsWith("ETBReplacement")
|| keyword.startsWith("CantBeBlockedBy ")
|| keyword.startsWith("Affinity")
|| keyword.equals("CARDNAME enters the battlefield tapped.")
|| keyword.startsWith("UpkeepCost")) {
} else if (keyword.equals("Provoke") || keyword.equals("Ingest") || keyword.equals("Unleash")
|| keyword.equals("Soulbond") || keyword.equals("Partner") || keyword.equals("Retrace")
|| keyword.equals("Living Weapon") || keyword.equals("Myriad") || keyword.equals("Exploit")
|| keyword.equals("Changeling") || keyword.equals("Delve")
|| keyword.equals("Split second") || keyword.equals("Sunburst")
|| keyword.equals("Suspend") // for the ones without amounnt
|| keyword.equals("Hideaway") || keyword.equals("Ascend")
|| keyword.equals("Totem armor") || keyword.equals("Battle cry")
|| keyword.equals("Devoid") || keyword.equals("Riot")){
sbLong.append(keyword + " (" + inst.getReminderText() + ")");
} else if (keyword.startsWith("Partner:")) {
final String[] k = keyword.split(":");
sbLong.append(k.length > 1 ? k[1] + ".\r\n" : "");
sbLong.append("Partner with " + k[1] + " (" + inst.getReminderText() + ")");
} else if (keyword.startsWith("Modular") || keyword.startsWith("Bloodthirst") || keyword.startsWith("Dredge")
|| keyword.startsWith("Fabricate") || keyword.startsWith("Soulshift") || keyword.startsWith("Bushido")
|| keyword.startsWith("Crew") || keyword.startsWith("Tribute") || keyword.startsWith("Absorb")
|| keyword.startsWith("Graft") || keyword.startsWith("Fading") || keyword.startsWith("Vanishing")
|| keyword.startsWith("Afterlife")
|| keyword.startsWith("Afflict") || keyword.startsWith ("Poisonous") || keyword.startsWith("Rampage")
|| keyword.startsWith("Renown") || keyword.startsWith("Annihilator") || keyword.startsWith("Devour")) {
final String[] k = keyword.split(":");
sbLong.append(k[0] + " " + k[1] + " (" + inst.getReminderText() + ")");
} else if (keyword.contains("Haunt")) {
sb.append("\r\nHaunt (");
if (isCreature()) {
sb.append("When this creature dies, exile it haunting target creature.");
} else {
sb.append("When this spell card is put into a graveyard after resolving, ");
sb.append("exile it haunting target creature.");
}
sb.append(")");
} else if (keyword.equals("Convoke") || keyword.equals("Dethrone")|| keyword.equals("Fear")
|| keyword.equals("Melee") || keyword.equals("Improvise")|| keyword.equals("Shroud")
|| keyword.equals("Banding") || keyword.equals("Intimidate")|| keyword.equals("Evolve")
|| keyword.equals("Exalted") || keyword.equals("Extort")|| keyword.equals("Flanking")
|| keyword.equals("Horsemanship") || keyword.equals("Infect")|| keyword.equals("Persist")
|| keyword.equals("Phasing") || keyword.equals("Shadow")|| keyword.equals("Skulk")
|| keyword.equals("Undying") || keyword.equals("Wither") || keyword.equals("Cascade")
|| keyword.equals("Mentor")) {
if (sb.length() != 0) {
sb.append("\r\n");
}
sb.append(keyword + " (" + inst.getReminderText() + ")");
} else if (keyword.endsWith(" offering")) {
String offeringType = keyword.split(" ")[0];
if (sb.length() != 0) {
sb.append("\r\n");
}
sbLong.append(keyword);
sbLong.append(" (" + Keyword.getInstance("Offering:"+ offeringType).getReminderText() + ")");
} else if (keyword.startsWith("Equip") || keyword.startsWith("Fortify") || keyword.startsWith("Outlast")
|| keyword.startsWith("Unearth") || keyword.startsWith("Scavenge") || keyword.startsWith("Spectacle")
|| keyword.startsWith("Evoke") || keyword.startsWith("Bestow") || keyword.startsWith("Dash")
|| keyword.startsWith("Surge") || keyword.startsWith("Transmute") || keyword.startsWith("Suspend")
|| keyword.equals("Undaunted") || keyword.startsWith("Monstrosity") || keyword.startsWith("Embalm")
|| keyword.startsWith("Level up") || keyword.equals("Prowess") || keyword.startsWith("Eternalize")
|| keyword.startsWith("Reinforce") || keyword.startsWith("Champion") || keyword.startsWith("Prowl")
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Adapt")
|| keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap")
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")) {
// keyword parsing takes care of adding a proper description
} else if (keyword.startsWith("CantBeBlockedByAmount")) {
sbLong.append(getName()).append(" can't be blocked ");
sbLong.append(getTextForKwCantBeBlockedByAmount(keyword));
} else if (keyword.startsWith("CantBlock")) {
sbLong.append(getName()).append(" can't block ");
if (keyword.contains("CardUID")) {
sbLong.append("CardID (").append(Integer.valueOf(keyword.split("CantBlockCardUID_")[1])).append(")");
} else {
final String[] k = keyword.split(":");
sbLong.append(k.length > 1 ? k[1] + ".\r\n" : "");
}
} else if (keyword.equals("Unblockable")) {
sbLong.append(getName()).append(" can't be blocked.\r\n");
} else if (keyword.equals("AllNonLegendaryCreatureNames")) {
sbLong.append(getName()).append(" has all names of nonlegendary creature cards.\r\n");
} else if (keyword.startsWith("IfReach")) {
String k[] = keyword.split(":");
sbLong.append(getName()).append(" can block ")
.append(CardType.getPluralType(k[1]))
.append(" as though it had reach.\r\n");
} else if (keyword.startsWith("MayEffectFromOpeningHand")) {
final String[] k = keyword.split(":");
// need to get SpellDescription from Svar
String desc = AbilityFactory.getMapParams(getSVar(k[1])).get("SpellDescription");
sbLong.append(desc);
} else if (keyword.startsWith("Saga")) {
String k[] = keyword.split(":");
String desc = "(As this Saga enters and after your draw step, "
+ " add a lore counter. Sacrifice after " + Strings.repeat("I", Integer.valueOf(k[1])) + ".)";
sbLong.append(desc);
}
} else if (keyword.equals("Unblockable")) {
sbLong.append(getName()).append(" can't be blocked.\r\n");
} else if (keyword.equals("AllNonLegendaryCreatureNames")) {
sbLong.append(getName()).append(" has all names of nonlegendary creature cards.\r\n");
} else if (keyword.startsWith("IfReach")) {
String k[] = keyword.split(":");
sbLong.append(getName()).append(" can block ")
.append(CardType.getPluralType(k[1]))
.append(" as though it had reach.\r\n");
} else if (keyword.startsWith("MayEffectFromOpeningHand")) {
final String[] k = keyword.split(":");
// need to get SpellDescription from Svar
String desc = AbilityFactory.getMapParams(getSVar(k[1])).get("SpellDescription");
sbLong.append(desc);
} else if (keyword.startsWith("Saga")) {
String k[] = keyword.split(":");
String desc = "(As this Saga enters and after your draw step, "
+ " add a lore counter. Sacrifice after " + Strings.repeat("I", Integer.valueOf(k[1])) + ".)";
sbLong.append(desc);
}
else {
if ((i != 0) && (sb.length() != 0)) {
sb.append(", ");
else {
if ((i != 0) && (sb.length() != 0)) {
sb.append(", ");
}
sb.append(keyword);
}
if (sbLong.length() > 0) {
sbLong.append("\r\n");
}
sb.append(keyword);
}
if (sbLong.length() > 0) {
sbLong.append("\r\n");
}
i++;
i++;
} catch (Exception e) {
String msg = "Card:keywordToText: crash in Keyword parsing";
Sentry.getContext().recordBreadcrumb(
new BreadcrumbBuilder().setMessage(msg)
.withData("Card", this.getName()).withData("Keyword", keyword).build()
);
throw new RuntimeException("Error in Card " + this.getName() + " with Keyword " + keyword, e);
}
}
if (sb.length() > 0) {
sb.append("\r\n");
@@ -2249,6 +2246,16 @@ public class Card extends GameEntity implements Comparable<Card> {
updateBasicLandAbilities(list, state);
}
// add Facedown abilities from Original state but only if this state is face down
// need CardStateView#getState or might crash in StackOverflow
if ((mana == null || mana == false) && isFaceDown() && state.getView().getState() == CardStateName.FaceDown) {
for (SpellAbility sa : getState(CardStateName.Original).getNonManaAbilities()) {
if (sa.isManifestUp() || sa.isMorphUp()) {
list.add(sa);
}
}
}
for (KeywordInterface kw : getUnhiddenKeywords(state)) {
for (SpellAbility sa : kw.getAbilities()) {
if (mana == null || mana == sa.isManaAbility()) {
@@ -3232,11 +3239,15 @@ public class Card extends GameEntity implements Comparable<Card> {
}
public final void tap() {
tap(false);
}
public final void tap(boolean attacker) {
if (tapped) { return; }
// Run triggers
final Map<String, Object> runParams = Maps.newTreeMap();
runParams.put("Card", this);
runParams.put("Attacker", attacker);
getGame().getTriggerHandler().runTrigger(TriggerType.Taps, runParams, false);
setTapped(true);
@@ -3395,6 +3406,15 @@ public class Card extends GameEntity implements Comparable<Card> {
return change;
}
public final boolean hasChangedCardKeywords(final long timestamp) {
return changedCardKeywords.containsKey(timestamp);
}
public final void addChangedCardKeywordsInternal(final KeywordsChange change, final long timestamp) {
changedCardKeywords.put(timestamp, change);
updateKeywordsCache(currentState);
}
// Hidden keywords will be left out
public final Collection<KeywordInterface> getUnhiddenKeywords() {
return getUnhiddenKeywords(currentState);
@@ -3643,10 +3663,8 @@ public class Card extends GameEntity implements Comparable<Card> {
if (s.startsWith("HIDDEN")) {
removeHiddenExtrinsicKeyword(s);
}
else {
if (extrinsicKeyword.remove(s)) {
currentState.getView().updateKeywords(this, currentState);
}
else if (extrinsicKeyword.remove(s)) {
currentState.getView().updateKeywords(this, currentState);
}
}
@@ -4812,6 +4830,15 @@ public class Card extends GameEntity implements Comparable<Card> {
// Note: This should only be called after state has been set to CardStateName.FaceDown,
// so the below call should be valid since the state should have been created already.
getState(CardStateName.FaceDown).setImageKey(ImageKeys.getTokenKey(image));
if (!manifested) {
// remove Manifest Up abilities from Original State
CardState original = getState(CardStateName.Original);
for (SpellAbility sa : original.getNonManaAbilities()) {
if (sa.isManifestUp()) {
original.removeSpellAbility(sa);
}
}
}
}
public final void animateBestow() {
@@ -5534,19 +5561,48 @@ public class Card extends GameEntity implements Comparable<Card> {
}
abilities.removeAll(toRemove);
if (getState(CardStateName.Original).getType().isLand()) {
if (getState(CardStateName.Original).getType().isLand() && !getLastKnownZone().is(ZoneType.Battlefield)) {
LandAbility la = new LandAbility(this, player, null);
if (la.canPlay()) {
abilities.add(la);
}
Card source = this;
boolean lkicheck = false;
// if Card is Facedown, need to check if MayPlay still applies
if (isFaceDown()) {
lkicheck = true;
source = CardUtil.getLKICopy(source);
// TODO need to be changed with CloneRewrite and FaceDownState?
source.turnFaceUp(false, false);
source.getCurrentState().copyFrom(getState(CardStateName.Original), true);
}
if (lkicheck) {
// double freeze tracker, so it doesn't update view
game.getTracker().freeze();
CardCollection preList = new CardCollection(source);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
}
// extra for MayPlay
for (CardPlayOption o : this.mayPlay(player)) {
for (CardPlayOption o : source.mayPlay(player)) {
la = new LandAbility(this, player, o.getAbility());
if (la.canPlay()) {
abilities.add(la);
}
}
// reset static abilities
if (lkicheck) {
game.getAction().checkStaticAbilities(false);
// clear delayed changes, this check should not have updated the view
game.getTracker().clearDelayed();
// need to unfreeze tracker
game.getTracker().unfreeze();
}
}
return abilities;
@@ -5715,7 +5771,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public void setChangedCardKeywords(Map<Long, KeywordsChange> changedCardKeywords) {
this.changedCardKeywords.clear();
for (Entry<Long, KeywordsChange> entry : changedCardKeywords.entrySet()) {
this.changedCardKeywords.put(entry.getKey(), entry.getValue());
this.changedCardKeywords.put(entry.getKey(), entry.getValue().copy(this, true));
}
}

View File

@@ -19,6 +19,13 @@ import forge.game.trigger.TriggerType;
public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
private Table<Card, GameEntity, Integer> dataMap = HashBasedTable.create();
public CardDamageMap(Table<Card, GameEntity, Integer> damageMap) {
this.putAll(damageMap);
}
public CardDamageMap() {
}
public void triggerPreventDamage(boolean isCombat) {
for (Map.Entry<GameEntity, Map<Card, Integer>> e : this.columnMap().entrySet()) {
int sum = 0;

View File

@@ -25,8 +25,6 @@ import forge.card.mana.ManaCost;
import forge.game.Game;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.ability.effects.CharmEffect;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.replacement.ReplacementHandler;
@@ -95,7 +93,6 @@ public class CardFactory {
out.setAttachedCards(in.getAttachedCards());
out.setEntityAttachedTo(in.getEntityAttachedTo());
out.setClones(in.getClones());
out.setCastSA(in.getCastSA());
for (final Object o : in.getRemembered()) {
out.addRemembered(o);
@@ -431,6 +428,9 @@ public class CardFactory {
private static void readCardFace(Card c, ICardFace face) {
// Name first so Senty has the Card name
c.setName(face.getName());
for (String r : face.getReplacements()) c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true));
for (String s : face.getStaticAbilities()) c.addStaticAbility(s);
for (String t : face.getTriggers()) c.addTrigger(TriggerHandler.parseTrigger(t, c, true));
@@ -440,7 +440,6 @@ public class CardFactory {
// keywords not before variables
c.addIntrinsicKeywords(face.getKeywords(), false);
c.setName(face.getName());
c.setManaCost(face.getManaCost());
c.setText(face.getNonAbilityText());
@@ -618,6 +617,9 @@ public class CardFactory {
}
if (from.getRestrictions() != null) {
to.setRestrictions((SpellAbilityRestriction) from.getRestrictions().copy());
if (!lki) {
to.getRestrictions().resetTurnActivations();
}
}
if (from.getConditions() != null) {
to.setConditions((SpellAbilityCondition) from.getConditions().copy());
@@ -676,9 +678,6 @@ public class CardFactory {
}
trig.setStackDescription(trig.toString());
if (trig.getApi() == ApiType.Charm && !trig.isWrapper()) {
CharmEffect.makeChoices(trig);
}
WrappedAbility wrapperAbility = new WrappedAbility(t, trig, ((WrappedAbility) sa).getDecider());
wrapperAbility.setTrigger(true);

View File

@@ -25,7 +25,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import forge.GameCommand;
import forge.card.*;
import forge.card.mana.ManaAtom;
import forge.card.mana.ManaCost;
@@ -136,27 +135,27 @@ public class CardFactoryUtil {
public static SpellAbility abilityMorphUp(final Card sourceCard, final String costStr, final boolean mega) {
Cost cost = new Cost(costStr, true);
String costDesc = cost.toString();
// get rid of the ": " at the end
costDesc = costDesc.substring(0, costDesc.length() - 2);
StringBuilder sbCost = new StringBuilder(mega ? "Megamorph" : "Morph");
sbCost.append(" ");
if (!cost.isOnlyManaCost()) {
costDesc = "" + costDesc;
sbCost.append(" ");
}
// get rid of the ": " at the end
sbCost.append(costDesc.substring(0, costDesc.length() - 2));
String ab = "ST$ SetState | Cost$ " + costStr + " | CostDesc$ Morph" + costDesc
+ " | MorphUp$ True"
+ " | ConditionDefined$ Self | ConditionPresent$ Card.faceDown"
+ " | Mode$ TurnFace | SpellDescription$ (Turn this face up any time for its morph cost.)";
StringBuilder sb = new StringBuilder();
sb.append("ST$ SetState | Cost$ ").append(costStr).append(" | CostDesc$ ").append(sbCost);
sb.append(" | MorphUp$ True | Secondary$ True | IsPresent$ Card.Self+faceDown");
if (mega) {
ab += " | Mega$ True";
sb.append(" | Mega$ True");
}
sb.append(" | Mode$ TurnFace | SpellDescription$ (Turn this face up any time for its morph cost.)");
final SpellAbility morphUp = AbilityFactory.getAbility(ab, sourceCard);
final SpellAbility morphUp = AbilityFactory.getAbility(sb.toString(), sourceCard);
final StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - turn this card face up.");
morphUp.setStackDescription(sbStack.toString());
morphUp.setIsMorphUp(true);
return morphUp;
}
@@ -166,18 +165,17 @@ public class CardFactoryUtil {
String costDesc = manaCost.toString();
// Cost need to be set later
String ab = "ST$ SetState | Cost$ 0 | CostDesc$ Unmanifest " + costDesc
+ " | ManifestUp$ True"
+ " | ConditionDefined$ Self | ConditionPresent$ Card.faceDown+manifested"
+ " | Mode$ TurnFace | SpellDescription$ (Turn this face up any time for its mana cost.)";
StringBuilder sb = new StringBuilder();
sb.append("ST$ SetState | Cost$ 0 | CostDesc$ Unmanifest ").append(costDesc);
sb.append(" | ManifestUp$ True | Secondary$ True | IsPresent$ Card.Self+faceDown+manifested");
sb.append(" | Mode$ TurnFace | SpellDescription$ (Turn this face up any time for its mana cost.)");
final SpellAbility manifestUp = AbilityFactory.getAbility(ab, sourceCard);
final SpellAbility manifestUp = AbilityFactory.getAbility(sb.toString(), sourceCard);
manifestUp.setPayCosts(new Cost(manaCost, true));
final StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - turn this card face up.");
manifestUp.setStackDescription(sbStack.toString());
manifestUp.setIsManifestUp(true);
return manifestUp;
}
@@ -1238,10 +1236,10 @@ public class CardFactoryUtil {
return doXMath(c.getPseudoKickerMagnitude(), m, c);
}
// Count$IfMainPhase.<numMain>.<numNotMain> // 7/10
if (sq[0].contains("IfMainPhase")) {
// Count$IfCastInOwnMainPhase.<numMain>.<numNotMain> // 7/10
if (sq[0].contains("IfCastInOwnMainPhase")) {
final PhaseHandler cPhase = cc.getGame().getPhaseHandler();
final boolean isMyMain = cPhase.getPhase().isMain() && cPhase.getPlayerTurn().equals(cc);
final boolean isMyMain = cPhase.getPhase().isMain() && cPhase.getPlayerTurn().equals(cc) && c.getCastFrom() != null;
return doXMath(Integer.parseInt(sq[isMyMain ? 1 : 2]), m, c);
}
@@ -2100,7 +2098,7 @@ public class CardFactoryUtil {
String abStr = "DB$ PutCounter | Defined$ Self | CounterType$ " + splitkw[1]
+ " | ETB$ True | CounterNum$ " + amount;
if (!StringUtils.isNumeric(amount)) {
if (!StringUtils.isNumeric(amount) && card.hasSVar(amount)) {
abStr += " | References$ " + amount;
}
@@ -2433,9 +2431,8 @@ public class CardFactoryUtil {
final String counter = "DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ " + n +
" | IsPresent$ Card.StrictlySelf | SpellDescription$ Put "
+ Lang.nounWithNumeral(n, "+1/+1 counter") + " on it.";
final String token = "DB$ Token | TokenAmount$ " + n + " | TokenName$ Servo | TokenTypes$ Artifact,Creature,Servo"
+ " | TokenOwner$ You | TokenColors$ Colorless | TokenPower$ 1 | TokenToughness$ 1"
+ " | TokenImage$ c 1 1 servo | TokenAltImages$ c_1_1_servo2,c_1_1_servo3 | SpellDescription$ Create "
final String token = "DB$ Token | TokenAmount$ " + n + " | TokenScript$ c_1_1_a_servo | TokenOwner$ You "
+ " | LegacyImage$ c 1 1 a servo aer | SpellDescription$ Create "
+ Lang.nounWithNumeral(n, "1/1 colorless Servo artifact creature token") + ".";
final Trigger trigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
@@ -2633,8 +2630,7 @@ public class CardFactoryUtil {
sbTrig.append("Living Weapon (" + inst.getReminderText() + ")");
final StringBuilder sbGerm = new StringBuilder();
sbGerm.append("DB$ Token | TokenAmount$ 1 | TokenName$ Germ | TokenTypes$ Creature,Germ | RememberTokens$ True | ");
sbGerm.append("TokenOwner$ You | TokenColors$ Black | TokenPower$ 0 | TokenToughness$ 0 | TokenImage$ B 0 0 Germ");
sbGerm.append("DB$ Token | TokenAmount$ 1 | TokenScript$ b_0_0_germ |TokenOwner$ You | RememberTokens$ True");
final SpellAbility saGerm = AbilityFactory.getAbility(sbGerm.toString(), card);
@@ -3453,12 +3449,10 @@ public class CardFactoryUtil {
sb.append(m);
sb.append(" (").append(inst.getReminderText()).append(")");
if ("Sunburst".equals(m)) {
card.setSVar(m, "Count$Converge");
}
final ReplacementEffect re = makeEtbCounter(sb.toString(), card, intrinsic);
if ("Sunburst".equals(m)) {
re.getOverridingAbility().setSVar("Sunburst", "Count$Converge");
}
inst.addReplacement(re);
} else if (keyword.equals("Rebound")) {
String repeffstr = "Event$ Moved | ValidCard$ Card.Self+wasCastFromHand+YouOwn+YouCtrl "
@@ -3513,6 +3507,19 @@ public class CardFactoryUtil {
String sb = "etbCounter:LORE:1:no Condition:no desc";
final ReplacementEffect re = makeEtbCounter(sb, card, intrinsic);
inst.addReplacement(re);
} else if (keyword.equals("Sunburst")) {
// Rule 702.43a If this object is entering the battlefield as a creature,
// ignoring any type-changing effects that would affect it
CounterType t = card.isCreature() ? CounterType.P1P1 : CounterType.CHARGE;
StringBuilder sb = new StringBuilder("etbCounter:");
sb.append(t).append(":Sunburst:no Condition:");
sb.append("Sunburst (").append(inst.getReminderText()).append(")");
final ReplacementEffect re = makeEtbCounter(sb.toString(), card, intrinsic);
re.getOverridingAbility().setSVar("Sunburst", "Count$Converge");
inst.addReplacement(re);
} else if (keyword.equals("Totem armor")) {
String repeffstr = "Event$ Destroy | ActiveZones$ Battlefield | ValidCard$ Card.EnchantedBy"
@@ -3704,13 +3711,24 @@ public class CardFactoryUtil {
final String[] k = keyword.split(":");
final String magnitude = k[1];
final String manacost = k[2];
final String reduceCost = k.length > 3 ? k[3] : null;
Set<String> references = Sets.newHashSet();
String desc = "Adapt " + magnitude;
String effect = "AB$ PutCounter | Cost$ " + manacost + " | ConditionPresent$ "
+ "Card.Self+counters_EQ0_P1P1 | Adapt$ True | CounterNum$ " + magnitude
String effect = "AB$ PutCounter | Cost$ " + manacost + " | Adapt$ True | CounterNum$ " + magnitude
+ " | CounterType$ P1P1 | StackDescription$ SpellDescription";
if (reduceCost != null) {
effect += "| ReduceCost$ " + reduceCost;
references.add(reduceCost);
desc += ". This ability costs {1} less to activate for each instant and sorcery card in your graveyard.";
}
if (!references.isEmpty()) {
effect += "| References$ " + TextUtil.join(references, ",");
}
effect += "| SpellDescription$ " + desc + " (" + inst.getReminderText() + ")";
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
@@ -3726,6 +3744,19 @@ public class CardFactoryUtil {
origSA.setAftermath(true);
origSA.getRestrictions().setZone(ZoneType.Graveyard);
// The Exile part is done by the System itself
} else if (keyword.startsWith("Aura swap")) {
final String[] k = keyword.split(":");
final String manacost = k[1];
final String effect = "AB$ ExchangeZone | Cost$ " + manacost + " | Zone2$ Hand | Type$ Aura "
+ " | PrecostDesc$ Aura swap | CostDesc$ " + ManaCostParser.parse(manacost)
+ " | StackDescription$ SpellDescription | SpellDescription$ (" + inst.getReminderText() + ")";
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa);
} else if (keyword.startsWith("Awaken")) {
final String[] k = keyword.split(":");
final String counters = k[1];
@@ -4037,22 +4068,12 @@ public class CardFactoryUtil {
final String[] k = keyword.split(":");
inst.addSpellAbility(abilityMorphDown(card));
CardState state = card.getState(CardStateName.FaceDown);
state.setSVars(card.getSVars());
KeywordInterface facedownKeyword = Keyword.getInstance("");
facedownKeyword.addSpellAbility(abilityMorphUp(card, k[1], false));
state.addIntrinsicKeywords(Lists.newArrayList(facedownKeyword));
inst.addSpellAbility(abilityMorphUp(card, k[1], false));
} else if (keyword.startsWith("Megamorph")){
final String[] k = keyword.split(":");
inst.addSpellAbility(abilityMorphDown(card));
CardState state = card.getState(CardStateName.FaceDown);
state.setSVars(card.getSVars());
KeywordInterface facedownKeyword = Keyword.getInstance("");
facedownKeyword.addSpellAbility(abilityMorphUp(card, k[1], true));
state.addIntrinsicKeywords(Lists.newArrayList(facedownKeyword));
inst.addSpellAbility(abilityMorphUp(card, k[1], true));
} else if (keyword.startsWith("Multikicker")) {
final String[] n = keyword.split(":");
final SpellAbility sa = card.getFirstSpellAbility();
@@ -4182,18 +4203,17 @@ public class CardFactoryUtil {
String effect = "AB$ PutCounter | Cost$ " + manacost + " ExileFromGrave<1/CARDNAME> " +
"| ActivationZone$ Graveyard | ValidTgts$ Creature | CounterType$ P1P1 " +
"| CounterNum$ ScavengeX | SorcerySpeed$ True | References$ ScavengeX " +
"| CounterNum$ ScavengeX | SorcerySpeed$ True " +
"| PrecostDesc$ Scavenge | CostDesc$ " + ManaCostParser.parse(manacost) +
"| SpellDescription$ (" + inst.getReminderText() + ")";
card.setSVar("ScavengeX", "Count$CardPower");
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setSVar("ScavengeX", "Count$CardPower");
sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa);
} else if (keyword.startsWith("Spectacle")) {
final String[] k = keyword.split(":");
final Cost cost = new Cost(k[1], false);
@@ -4211,28 +4231,6 @@ public class CardFactoryUtil {
newSA.setTemporary(!intrinsic);
inst.addSpellAbility(newSA);
} else if (keyword.equals("Sunburst") && intrinsic) {
final GameCommand sunburstCIP = new GameCommand() {
private static final long serialVersionUID = 1489845860231758299L;
@Override
public void run() {
CounterType t = card.isCreature() ? CounterType.P1P1 : CounterType.CHARGE;
card.addCounter(t, card.getSunburstValue(), card.getController(), true);
}
};
final GameCommand sunburstLP = new GameCommand() {
private static final long serialVersionUID = -7564420917490677427L;
@Override
public void run() {
card.setSunburstValue(0);
}
};
card.addComesIntoPlayCommand(sunburstCIP);
card.addLeavesPlayCommand(sunburstLP);
} else if (keyword.startsWith("Surge")) {
final String[] k = keyword.split(":");
final Cost surgeCost = new Cost(k[1], false);
@@ -4299,17 +4297,33 @@ public class CardFactoryUtil {
suspend.setTemporary(!intrinsic);
inst.addSpellAbility(suspend);
} else if (keyword.startsWith("Transfigure")) {
final String[] k = keyword.split(":");
final String manacost = k[1];
final String effect = "AB$ ChangeZone | Cost$ " + manacost + " Sac<1/CARDNAME>"
+ " | PrecostDesc$ Transfigure | CostDesc$ " + ManaCostParser.parse(manacost)
+ " | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.cmcEQTransfigureX"
+ " | ChangeNum$ 1 | SorcerySpeed$ True | StackDescription$ SpellDescription | SpellDescription$ ("
+ inst.getReminderText() + ")";
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setSVar("TransfigureX", "Count$CardManaCost");
sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);
inst.addSpellAbility(sa);
} else if (keyword.startsWith("Transmute")) {
final String[] k = keyword.split(":");
final String manacost = k[1];
final String effect = "AB$ ChangeZone | Cost$ " + manacost + " Discard<1/CARDNAME>"
+ " | PrecostDesc$ Transmute | CostDesc$ " + ManaCostParser.parse(manacost) + " | ActivationZone$ Hand"
+ " | Origin$ Library | Destination$ Hand | ChangeType$ Card.cmcEQ" + card.getManaCost().getCMC()
+ " | Origin$ Library | Destination$ Hand | ChangeType$ Card.cmcEQTransmuteX"
+ " | ChangeNum$ 1 | SorcerySpeed$ True | StackDescription$ SpellDescription | SpellDescription$ ("
+ inst.getReminderText() + ")";
final SpellAbility sa = AbilityFactory.getAbility(effect, card);
sa.setSVar("TransmuteX", "Count$CardManaCost");
sa.setIntrinsic(intrinsic);
sa.setTemporary(!intrinsic);

View File

@@ -967,6 +967,19 @@ public class CardProperty {
if (restriction.startsWith("Remembered") || restriction.startsWith("Imprinted")) {
CardCollection list = AbilityUtils.getDefinedCards(source, restriction, spellAbility);
return CardLists.filter(list, CardPredicates.sharesNameWith(card)).isEmpty();
} else if (restriction.equals("YourGraveyard")) {
return CardLists.filter(sourceController.getCardsIn(ZoneType.Graveyard), CardPredicates.sharesNameWith(card)).isEmpty();
} else if (restriction.equals("OtherYourBattlefield")) {
// Obviously it's going to share a name with itself, so consider that in the
CardCollection list = CardLists.filter(sourceController.getCardsIn(ZoneType.Battlefield), CardPredicates.sharesNameWith(card));
if (list.size() == 1) {
Card c = list.getFirst();
if (c.getTimestamp() == card.getTimestamp() && c.getId() == card.getId()) {
list.remove(card);
}
}
return list.isEmpty();
}
}
} else if (property.startsWith("sharesControllerWith")) {
@@ -1239,7 +1252,7 @@ public class CardProperty {
} else if (property.startsWith("greatestPower")) {
CardCollectionView cards = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
if (property.contains("ControlledBy")) {
FCollectionView<Player> p = AbilityUtils.getDefinedPlayers(source, property.split("ControlledBy")[1], null);
FCollectionView<Player> p = AbilityUtils.getDefinedPlayers(source, property.split("ControlledBy")[1], spellAbility);
cards = CardLists.filterControlledBy(cards, p);
if (!cards.contains(card)) {
return false;

View File

@@ -43,6 +43,9 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import io.sentry.Sentry;
import io.sentry.event.BreadcrumbBuilder;
public class CardState extends GameObject {
private String name = "";
private CardType type = new CardType();
@@ -69,6 +72,10 @@ public class CardState extends GameObject {
private final CardStateView view;
private final Card card;
public CardState(Card card, CardStateName name) {
this(card.getView().createAlternateState(name), card);
}
public CardState(CardStateView view0, Card card0) {
view = view0;
card = card0;
@@ -209,7 +216,19 @@ public class CardState extends GameObject {
if (s.trim().length() == 0) {
return null;
}
KeywordInterface inst = intrinsicKeywords.add(s);
KeywordInterface inst = null;
try {
inst = intrinsicKeywords.add(s);
} catch (Exception e) {
String msg = "CardState:addIntrinsicKeyword: failed to parse Keyword";
Sentry.getContext().recordBreadcrumb(
new BreadcrumbBuilder().setMessage(msg)
.withData("Card", card.getName()).withData("Keyword", s).build()
);
//rethrow
throw new RuntimeException("Error in Keyword " + s + " for card " + card.getName(), e);
}
if (inst != null && initTraits) {
inst.createTraits(card, true);
}

View File

@@ -65,7 +65,7 @@ public final class CardUtil {
"Transmute", "Replicate", "Recover", "Suspend", "Aura swap",
"Fortify", "Transfigure", "Champion", "Evoke", "Prowl", "IfReach",
"Reinforce", "Unearth", "Level up", "Miracle", "Overload",
"Scavenge", "Bestow", "Outlast", "Dash", "Renown", "Surge", "Emerge", "Hexproof:").build();
"Scavenge", "Bestow", "Outlast", "Dash", "Surge", "Emerge", "Hexproof:").build();
/** List of keyword endings of keywords that could be modified by text changes. */
public static final ImmutableList<String> modifiableKeywordEndings = ImmutableList.<String>builder().add(
"walk", "cycling", "offering").build();
@@ -269,7 +269,6 @@ public final class CardUtil {
newCopy.setAttachedCards(in.getAttachedCards());
newCopy.setEntityAttachedTo(in.getEntityAttachedTo());
newCopy.setClones(in.getClones());
newCopy.setHaunting(in.getHaunting());
newCopy.setCopiedPermanent(in.getCopiedPermanent());
for (final Card haunter : in.getHauntedBy()) {

View File

@@ -11,6 +11,7 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import forge.game.Game;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
@@ -45,4 +46,39 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
}
}
public CardCollection filterCards(Iterable<ZoneType> origin, ZoneType destination, String valid, Card host, SpellAbility sa) {
CardCollection allCards = new CardCollection();
if (destination != null) {
if (!containsColumn(destination)) {
return allCards;
}
}
if (origin != null) {
for (ZoneType z : origin) {
if (containsRow(z)) {
if (destination != null) {
allCards.addAll(row(z).get(destination));
} else {
for (CardCollection c : row(z).values()) {
allCards.addAll(c);
}
}
}
}
} else if (destination != null) {
for (CardCollection c : column(destination).values()) {
allCards.addAll(c);
}
} else {
for (CardCollection c : values()) {
allCards.addAll(c);
}
}
if (valid != null) {
allCards = CardLists.getValidCards(allCards, valid.split(","), host.getController(), host, sa);
}
return allCards;
}
}

View File

@@ -9,6 +9,7 @@ import forge.StaticData;
import forge.card.CardType;
import forge.card.MagicColor;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardFactory;
import forge.game.card.CardFactoryUtil;
@@ -17,12 +18,11 @@ import forge.game.keyword.KeywordInterface;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.item.PaperToken;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
public class TokenInfo {
final String name;
final String imageName;
@@ -128,8 +128,6 @@ public class TokenInfo {
c.setName(name);
c.setImageKey(ImageKeys.getTokenKey(imageName));
// TODO - most tokens mana cost is 0, this needs to be fixed
// c.setManaCost(manaCost);
c.setColor(color.isEmpty() ? manaCost : color);
c.setToken(true);
@@ -226,6 +224,10 @@ public class TokenInfo {
}
static public Card getProtoType(final String script, final SpellAbility sa) {
// script might be null, or sa might be null
if (script == null || sa == null) {
return null;
}
final Card host = sa.getHostCard();
final Game game = host.getGame();
@@ -235,6 +237,18 @@ public class TokenInfo {
if (token != null) {
final Card result = Card.fromPaperCard(token, null, game);
if (sa.hasParam("TokenPower")) {
String str = sa.getParam("TokenPower");
result.setBasePowerString(str);
result.setBasePower(AbilityUtils.calculateAmount(host, str, sa));
}
if (sa.hasParam("TokenToughness")) {
String str = sa.getParam("TokenToughness");
result.setBaseToughnessString(str);
result.setBaseToughness(AbilityUtils.calculateAmount(host, str, sa));
}
// update Token with CardTextChanges
Map<String, String> colorMap = sa.getChangedTextColors();
Map<String, String> typeMap = sa.getChangedTextTypes();

View File

@@ -816,6 +816,7 @@ public class Combat {
}
preventMap.triggerPreventDamage(true);
preventMap.clear();
// This was deeper before, but that resulted in the stack entry acting like before.
// Run the trigger to deal combat damage once

View File

@@ -43,7 +43,6 @@ import forge.util.TextUtil;
import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
import forge.util.maps.MapToAmount;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
@@ -525,7 +524,7 @@ public class CombatUtil {
IGNORE_LANDWALK_KEYWORDS[i] = "May be blocked as though it doesn't have " + landwalk + ".";
}
}
public static boolean isUnblockableFromLandwalk(final Card attacker, final Player defendingPlayer) {
//May be blocked as though it doesn't have landwalk. (Staff of the Ages)
if (attacker.hasKeyword("May be blocked as though it doesn't have landwalk.")) {
@@ -534,6 +533,7 @@ public class CombatUtil {
List<String> walkTypes = Lists.newArrayList();
// handle basic landwalk and snow basic landwalk
for (int i = 0; i < LANDWALK_KEYWORDS.length; i++) {
final String basic = MagicColor.Constant.BASIC_LANDS.get(i);
final String landwalk = LANDWALK_KEYWORDS[i];
@@ -553,19 +553,24 @@ public class CombatUtil {
String keyword = inst.getOriginal();
if (keyword.equals("Legendary landwalk")) {
walkTypes.add("Land.Legendary");
} else if (keyword.equals("Desertwalk")) {
walkTypes.add("Desert");
} else if (keyword.equals("Nonbasic landwalk")) {
walkTypes.add("Land.nonBasic");
} else if (keyword.equals("Snow landwalk")) {
walkTypes.add("Land.Snow");
} else if (keyword.endsWith("walk")) {
final String landtype = TextUtil.fastReplace(keyword, "walk", "");
String landtype = TextUtil.fastReplace(keyword, "walk", "");
String valid = landtype;
// substract Snow type
if (landtype.startsWith("Snow ")) {
walkTypes.add(landtype.substring(5) + ".Snow");
} else if (CardType.isALandType(landtype)) {
landtype = landtype.substring(5);
valid = landtype + ".Snow";
}
// basic land types are handled before
if (CardType.isALandType(landtype) && !CardType.isABasicLandType(landtype)) {
if (!walkTypes.contains(landtype)) {
walkTypes.add(landtype);
walkTypes.add(valid);
}
}
}
@@ -575,10 +580,10 @@ public class CombatUtil {
return false;
}
final String valid = StringUtils.join(walkTypes, ",");
final String[] valid = walkTypes.toArray(new String[0]);
final CardCollectionView defendingLands = defendingPlayer.getCardsIn(ZoneType.Battlefield);
for (final Card c : defendingLands) {
if (c.isValid(valid.split(","), defendingPlayer, attacker, null)) {
if (c.isValid(valid, defendingPlayer, attacker, null)) {
return true;
}
}

View File

@@ -366,10 +366,9 @@ public class CostAdjustment {
if (manaCost.toString().equals("{0}")) {
return 0;
}
final Map<String, String> params = staticAbility.getMapParams();
final Card hostCard = staticAbility.getHostCard();
final Card card = sa.getHostCard();
final String amount = params.get("Amount");
final String amount = staticAbility.getParam("Amount");
if (!checkRequirement(sa, staticAbility)) {
return 0;
@@ -380,14 +379,16 @@ public class CostAdjustment {
value = CardFactoryUtil.xCount(card, hostCard.getSVar(amount));
} else if ("Undaunted".equals(amount)) {
value = card.getController().getOpponents().size();
} else if (staticAbility.hasParam("Relative")) {
value = AbilityUtils.calculateAmount(hostCard, amount, sa);
} else {
value = AbilityUtils.calculateAmount(hostCard, amount, staticAbility);
}
if (!params.containsKey("Cost") && ! params.containsKey("Color")) {
if (!staticAbility.hasParam("Cost") && ! staticAbility.hasParam("Color")) {
int minMana = 0;
if (params.containsKey("MinMana")) {
minMana = Integer.valueOf(params.get("MinMana"));
if (staticAbility.hasParam("MinMana")) {
minMana = Integer.valueOf(staticAbility.getParam("MinMana"));
}
final int maxReduction = Math.max(0, manaCost.getConvertedManaCost() - minMana);
@@ -395,7 +396,7 @@ public class CostAdjustment {
return Math.min(value, maxReduction);
}
} else {
final String color = params.containsKey("Cost") ? params.get("Cost") : params.get("Color");
final String color = staticAbility.getParamOrDefault("Cost", staticAbility.getParam("Color"));
int sumGeneric = 0;
// might be problematic for wierd hybrid combinations
for (final String cost : color.split(" ")) {

View File

@@ -23,7 +23,7 @@ import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
/**
* The Class CostPayLife.
* The Class CostDamage.
*/
public class CostDamage extends CostPart {
@@ -74,6 +74,8 @@ public class CostDamage extends CostPart {
preventMap.triggerPreventDamage(false);
damageMap.triggerDamageDoneOnce(false, sa);
preventMap.clear();
damageMap.clear();
return decision.c > 0;
}

View File

@@ -25,7 +25,7 @@ import java.util.ArrayList;
import java.util.List;
/**
* The Class CostPayLife.
* The Class CostDraw.
*/
public class CostDraw extends CostPart {
/**

View File

@@ -18,7 +18,6 @@
package forge.game.cost;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -57,17 +56,6 @@ public class CostPayLife extends CostPart {
return sb.toString();
}
/*
* (non-Javadoc)
*
* @see forge.card.cost.CostPart#refund(forge.Card)
*/
@Override
public final void refund(final Card source) {
// Really should be activating player
source.getController().payLife(this.paidAmount * -1, null);
}
/*
* (non-Javadoc)
*

View File

@@ -1,8 +1,11 @@
package forge.game.event;
import forge.game.player.Player;
// This special event denotes loss of mana due to phase end
public class GameEventManaBurn extends GameEvent {
public final Player player;
public final boolean causedLifeLoss;
public final int amount;
@@ -11,8 +14,9 @@ public class GameEventManaBurn extends GameEvent {
* @param dealDamage
* @param burn
*/
public GameEventManaBurn(int burn, boolean dealDamage) {
amount = burn;
public GameEventManaBurn(Player who, int burn, boolean dealDamage) {
player = who;
amount = burn;
causedLifeLoss = dealDamage;
}

View File

@@ -8,6 +8,8 @@ import java.util.Iterator;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import forge.game.card.Card;
public class KeywordCollection implements Iterable<String>, Serializable {
private static final long serialVersionUID = -2882986558147844702L;
@@ -151,6 +153,12 @@ public class KeywordCollection implements Iterable<String>, Serializable {
return map.get(keyword);
}
public void setHostCard(final Card host) {
for (KeywordInterface k : map.values()) {
k.setHostCard(host);
}
}
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {

View File

@@ -202,7 +202,7 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
public Collection<StaticAbility> getStaticAbilities() {
return staticAbilities;
}
/*
* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#copy()
@@ -233,7 +233,7 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
return result;
} catch (final Exception ex) {
throw new RuntimeException("KeywordInstance : clone() error, " + ex);
throw new RuntimeException("KeywordInstance : clone() error", ex);
}
}
@@ -252,4 +252,26 @@ public abstract class KeywordInstance<T extends KeywordInstance<?>> implements K
public boolean redundant(Collection<KeywordInterface> list) {
return !list.isEmpty() && keyword.isMultipleRedundant;
}
/* (non-Javadoc)
* @see forge.game.keyword.KeywordInterface#setHostCard(forge.game.card.Card)
*/
@Override
public void setHostCard(Card host) {
for (SpellAbility sa : this.abilities) {
sa.setHostCard(host);
}
for (Trigger tr : this.triggers) {
tr.setHostCard(host);
}
for (ReplacementEffect re : this.replacements) {
re.setHostCard(host);
}
for (StaticAbility sa : this.staticAbilities) {
sa.setHostCard(host);
}
}
}

View File

@@ -31,6 +31,7 @@ public interface KeywordInterface extends Cloneable {
public void addSpellAbility(final SpellAbility s);
public void addStaticAbility(final StaticAbility st);
public void setHostCard(final Card host);
/**
* @return the triggers

View File

@@ -30,12 +30,11 @@ import forge.game.card.Card;
* </p>
*
* @author Forge
* @version $Id: KeywordsChange.java 27095 2014-08-17 07:32:24Z elcnesh $
*/
public class KeywordsChange {
private final KeywordCollection keywords = new KeywordCollection();
private final List<KeywordInterface> removeKeywordInterfaces = Lists.newArrayList();
private final List<String> removeKeywords = Lists.newArrayList();
public class KeywordsChange implements Cloneable {
private KeywordCollection keywords = new KeywordCollection();
private List<KeywordInterface> removeKeywordInterfaces = Lists.newArrayList();
private List<String> removeKeywords = Lists.newArrayList();
private boolean removeAllKeywords;
private boolean removeIntrinsicKeywords;
@@ -63,7 +62,7 @@ public class KeywordsChange {
this.removeAllKeywords = removeAll;
this.removeIntrinsicKeywords = removeIntrinsic;
}
public KeywordsChange(
final Collection<KeywordInterface> keywordList,
final Collection<KeywordInterface> removeKeywordInterfaces,
@@ -172,4 +171,49 @@ public class KeywordsChange {
removeIntrinsicKeywords = true;
}
}
public void setHostCard(final Card host) {
keywords.setHostCard(host);
for (KeywordInterface k : removeKeywordInterfaces) {
k.setHostCard(host);
}
}
public KeywordsChange copy(final Card host, final boolean lki) {
try {
KeywordsChange result = (KeywordsChange)super.clone();
result.keywords = new KeywordCollection();
for (KeywordInterface ki : this.keywords.getValues()) {
result.keywords.insert(ki.copy(host, lki));
}
result.removeKeywords = Lists.newArrayList(removeKeywords);
result.removeKeywordInterfaces = Lists.newArrayList();
for (KeywordInterface ki : this.removeKeywordInterfaces) {
removeKeywordInterfaces.add(ki.copy(host, lki));
}
return result;
} catch (final Exception ex) {
throw new RuntimeException("KeywordsChange : clone() error", ex);
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("<+");
sb.append(this.keywords);
sb.append("|-");
sb.append(this.removeKeywordInterfaces);
sb.append("|-");
sb.append(this.removeKeywords);
sb.append(">");
return sb.toString();
}
}

View File

@@ -389,7 +389,6 @@ public class PhaseHandler implements java.io.Serializable {
}
playerTurn.removeKeyword("Skip all combat phases of this turn.");
game.getCleanup().executeUntil(getNextTurn());
nUpkeepsThisTurn = 0;
// Rule 514.3
@@ -397,6 +396,9 @@ public class PhaseHandler implements java.io.Serializable {
// Rule 514.3a - state-based actions
game.getAction().checkStateEffects(true);
// done this after check state effects, so it only has effect next check
game.getCleanup().executeUntil(getNextTurn());
break;
default:
@@ -433,11 +435,7 @@ public class PhaseHandler implements java.io.Serializable {
boolean manaBurns = game.getRules().hasManaBurn();
if (manaBurns) {
p.loseLife(burn);
}
// Play the Mana Burn sound
if (burn > 0) {
game.fireEvent(new GameEventManaBurn(burn, manaBurns));
p.loseLife(burn,true);
}
}
@@ -526,7 +524,7 @@ public class PhaseHandler implements java.io.Serializable {
if (canAttack) {
if (shouldTapForAttack) {
attacker.tap();
attacker.tap(true);
}
} else {
combat.removeFromCombat(attacker);
@@ -722,6 +720,7 @@ public class PhaseHandler implements java.io.Serializable {
runParams.put("Attacker", a);
runParams.put("Blockers", blockers);
runParams.put("NumBlockers", blockers.size());
runParams.put("Defender", combat.getDefenderByAttacker(a));
runParams.put("DefendingPlayer", combat.getDefenderPlayerByAttacker(a));
game.getTriggerHandler().runTrigger(TriggerType.AttackerBlocked, runParams, false);

View File

@@ -455,8 +455,12 @@ public class Player extends GameEntity implements Comparable<Player> {
}
return true;
}
public final int loseLife(final int toLose) {
return loseLife(toLose,false);
}
public final int loseLife(final int toLose, final boolean manaBurn) {
int lifeLost = 0;
if (!canLoseLife()) {
return 0;
@@ -466,7 +470,11 @@ public class Player extends GameEntity implements Comparable<Player> {
life -= toLose;
view.updateLife(this);
lifeLost = toLose;
game.fireEvent(new GameEventPlayerLivesChanged(this, oldLife, life));
if(manaBurn) {
game.fireEvent(new GameEventManaBurn(this,lifeLost,true));
} else {
game.fireEvent(new GameEventPlayerLivesChanged(this, oldLife, life));
}
}
else if (toLose == 0) {
// Rule 118.4
@@ -515,14 +523,15 @@ public class Player extends GameEntity implements Comparable<Player> {
return false;
}
if (lifePayment <= 0)
return true;
loseLife(lifePayment);
// rule 118.8
if (life >= lifePayment) {
return (loseLife(lifePayment) > 0);
}
return false;
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", this);
runParams.put("LifeAmount", lifePayment);
game.getTriggerHandler().runTrigger(TriggerType.PayLife, runParams, false);
return true;
}
public final boolean canPayEnergy(final int energyPayment) {
@@ -1261,42 +1270,6 @@ public class Player extends GameEntity implements Comparable<Player> {
return drawCards(1);
}
public void scry(final int numScry, SpellAbility cause) {
final CardCollection topN = new CardCollection(this.getCardsIn(ZoneType.Library, numScry));
if (topN.isEmpty()) {
return;
}
final ImmutablePair<CardCollection, CardCollection> lists = getController().arrangeForScry(topN);
final CardCollection toTop = lists.getLeft();
final CardCollection toBottom = lists.getRight();
int numToBottom = 0;
int numToTop = 0;
if (toBottom != null) {
for(Card c : toBottom) {
getGame().getAction().moveToBottomOfLibrary(c, cause, null);
numToBottom++;
}
}
if (toTop != null) {
Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus.
for(Card c : toTop) {
getGame().getAction().moveToLibrary(c, cause, null);
numToTop++;
}
}
getGame().fireEvent(new GameEventScry(this, numToTop, numToBottom));
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", this);
getGame().getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false);
}
public void surveil(int num, SpellAbility cause) {
final Map<String, Object> repParams = Maps.newHashMap();

View File

@@ -13,8 +13,8 @@ public enum PlayerActionConfirmMode {
ChangeZoneGeneral,
BidLife,
OptionalChoose,
Tribute;
Tribute,
// Ripple;
;
}
}

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