diff --git a/BotWrapper/bot.conf b/BotWrapper/bot.conf index 7c276289..f04ec2c1 100644 --- a/BotWrapper/bot.conf +++ b/BotWrapper/bot.conf @@ -14,9 +14,19 @@ Random=AI_LV2 一些比较弱的卡组。 SUPPORT_MASTER_RULE_2020 -!随机-普通 +!随机-一般 Random=AI_LV3 -一些环境里可以看到的卡组。 +一些不是很弱的卡组。 +SUPPORT_MASTER_RULE_2020 + +!随机-普通 +Random=AI_LV4 +一些可以(或者曾经可以)在竞技环境里看到的卡组。 +SUPPORT_MASTER_RULE_2020 + +!随机-狂野 +Random=AI_WILD +普通的人机的卡组会符合其使用的卡池对应的环境的禁限卡表,但狂野模式的人机的卡组一般不符合任何禁限卡表。 SUPPORT_MASTER_RULE_2020 !随机-报社 @@ -67,17 +77,17 @@ AI_LV2 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 !琪露诺-饼蛙 Name=琪露诺 Deck='Toadally Awesome' Dialog=cirno.zh-CN 大师规则三的全盛饼蛙卡组。 -SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 +AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 !复制植物-青眼 Name=复制植物 Deck=Blue-Eyes Dialog=copy.zh-CN 青眼卡组。 -AI_LV2 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 +AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 !复制植物-十二兽 Name=复制植物 Deck=Zoodiac Dialog=copy.zh-CN 大师规则三的十四兽卡组。 -SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 +AI_LV4 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 !尼亚-妖仙兽 Name=尼亚 Deck=Yosenju Dialog=near.zh-CN @@ -92,12 +102,27 @@ AI_ANTI_META SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2 !尼亚-淘气仙星 Name=尼亚 Deck=Trickstar Dialog=near.zh-CN 旧式淘气仙星卡组。 -AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +AI_LV4 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !尼亚-幻变骚灵 Name=尼亚 Deck=Altergeist Dialog=near.zh-CN 幻变骚灵卡组。 -AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +AI_LV4 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 + +!尼亚-拉比林斯迷宫 +Name=尼亚 Deck=Labrynth Dialog=near.zh-CN +拉比林斯迷宫卡组。 +AI_LV4 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 + +!尼亚-于贝尔 +Name=尼亚 Deck=Yubel Dialog=near.zh-CN +于贝尔卡组。 +AI_LV4 SUPPORT_MASTER_RULE_2020 + +!尼亚-王家的神殿 +Name=尼亚 Deck=Apophis Dialog=near.zh-CN +王家的神殿卡组。 +AI_LV4 SUPPORT_MASTER_RULE_2020 !复制梁龙-闪刀姬 Name=复制梁龙 Deck=SkyStriker Dialog=anothercopy.zh-CN @@ -107,13 +132,23 @@ AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !复制梁龙-自奏圣乐 Name=复制梁龙 Deck=Orcust Dialog=anothercopy.zh-CN 旧式自奏圣乐卡组。 -AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +AI_LV4 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !复制梁龙-转生炎兽 Name=复制梁龙 Deck=Salamangreat Dialog=anothercopy.zh-CN 转生炎兽卡组。 AI_LV3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +!复制梁龙-雷火沸动 +Name=复制梁龙 Deck=Ryzeal Dialog=anothercopy.zh-CN +初代雷火沸动卡组。 +AI_LV4 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 + +!复制梁龙-码丽丝 +Name=复制梁龙 Deck=Maliss Dialog=anothercopy.zh-CN +MD全盛码丽丝卡组。 +AI_LV4 SUPPORT_MASTER_RULE_2020 + !VI-1911-凭依装着 Name=VI-1911 Deck=FamiliarPossessed Dialog=VI-1911.zh-CN 凭依装着卡组。 @@ -147,7 +182,7 @@ AI_LV2 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !试作型机器人1732 Name=试作型机器人1732 Deck=ST1732 Dialog=zh-CN 由三盒ST17和三盒SD32组成的卡组。 -AI_LV2 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +AI_LV1 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !奇異果 Name=奇異果 Deck=LightswornShaddoldinosour Dialog=kiwi.zh-TW @@ -162,7 +197,7 @@ AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !MAX龍果 Name=MAX龍果 Deck=BlueEyesMaxDragon Dialog=kiwi.zh-TW 青眼混沌极龙卡组。 -AI_LV2 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !幻煌果 Name=幻煌果 Deck=Phantasm Dialog=kiwi.zh-TW @@ -182,29 +217,59 @@ AI_ANTI_META SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2 !我太帅了-真红眼龙骑士 Name=我太帅了 Deck=Dragun Dialog=smart.zh-CN 超魔导真红眼龙骑士卡组。 -AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +AI_LV4 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !我太帅了-水百凤凰勇者 Name=我太帅了 Deck=Brave Dialog=smart.zh-CN 水机百头龙凤凰人勇者卡组。 -AI_LV3 SUPPORT_MASTER_RULE_2020 +AI_LV4 SUPPORT_MASTER_RULE_2020 -!玻璃女巫 +!玻璃女巫-魔女术 Name=玻璃女巫 Deck=Witchcraft Dialog=verre.zh-CN 魔女术卡组。 -AI_LV3 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 +AI_LV4 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 + +!玻璃女巫-救祓少女 +Name=玻璃女巫 Deck=Exosister Dialog=verre.zh-CN +救祓少女卡组。 +AI_LV4 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 + +!艾克莉西娅-相剑 +Name=艾克莉西娅 Deck=Swordsoul Dialog=ecclesia.zh-CN +相剑卡组。 +AI_LV4 SUPPORT_MASTER_RULE_3 SUPPORT_MASTER_RULE_2020 + +!艾克莉西娅-教导 +Name=艾克莉西娅 Deck=Dogmatika Dialog=ecclesia.zh-CN +教导卡组。 +AI_LV4 SUPPORT_MASTER_RULE_3 SUPPORT_NEW_MASTER_RULE SUPPORT_MASTER_RULE_2020 !神数不神-刹帝利 Name=神数不神 Deck=Kashtira Dialog=Zefra.zh-CN 俱舍怒威族卡组。 -AI_LV3 SUPPORT_MASTER_RULE_2020 +AI_LV4 SUPPORT_MASTER_RULE_2020 -![狂野模式]神数不神-雷龙 +![狂野]神数不神-雷龙 Name=神数不神 Deck=ThunderDragon Dialog=Zefra.zh-CN -深渊混沌雷龙卡组。(普通模式的人机的卡组会符合其使用的卡池对应的环境的禁限卡表,但在狂野模式中,人机的卡组不符合任何禁限卡表。) -SUPPORT_MASTER_RULE_2020 +深渊混沌雷龙卡组。 +AI_WILD SUPPORT_MASTER_RULE_2020 !神数不神-珠泪哀歌 Name=神数不神 Deck=Tearlaments Dialog=Zefra.zh-CN 旧式地天使珠泪哀歌族卡组。 -AI_LV3 SUPPORT_MASTER_RULE_2020 +AI_LV4 AI_WILD SUPPORT_MASTER_RULE_2020 + +!神数不神-神数 +Name=神数不神 Deck=Zefra Dialog=Zefra.zh-CN +神数卡组。 +AI_LV4 SUPPORT_MASTER_RULE_2020 + +!今晚有宵夜吗-超重武者 +Name=今晚有宵夜吗 Deck=SuperheavySamurai Dialog=superheavysamurai.zh-CN +超重武者卡组。 +AI_LV4 SUPPORT_MASTER_RULE_2020 + +!今晚有宵夜吗-码丽丝 +Name=今晚有宵夜吗 Deck=MalissOCG Dialog=Xiaoye.zh-CN +码丽丝卡组。 +AI_LV4 SUPPORT_MASTER_RULE_2020 \ No newline at end of file diff --git a/Decks/AI_Albaz.ydk b/Decks/AI_Albaz.ydk new file mode 100644 index 00000000..19a1b0bf --- /dev/null +++ b/Decks/AI_Albaz.ydk @@ -0,0 +1,64 @@ +#created by ... +#main +32731036 +25451383 +60242223 +62962630 +62962630 +62962630 +68468459 +68468459 +45484331 +45883110 +95515789 +19096726 +14558127 +14558127 +14558127 +23434538 +23434538 +23434538 +36577931 +1984618 +1984618 +6498706 +6498706 +34995106 +44362883 +75500286 +81439173 +24224830 +24224830 +29948294 +36637374 +65681983 +82738008 +18973184 +10045474 +10045474 +10045474 +19271881 +32756828 +17751597 +#extra +11321089 +38811586 +44146295 +44146295 +92892239 +70534340 +3410461 +24915933 +72272462 +1906812 +41373230 +51409648 +87746184 +87746184 +53971455 +!side +56787189 +55273561 +68468460 +72554664 +95515790 \ No newline at end of file diff --git a/Decks/AI_Apophis.ydk b/Decks/AI_Apophis.ydk new file mode 100644 index 00000000..f45ad0a8 --- /dev/null +++ b/Decks/AI_Apophis.ydk @@ -0,0 +1,59 @@ +#created by ... +#main +62514770 +60411677 +60411677 +63198739 +63198739 +97522863 +97522863 +97522863 +49238328 +49238328 +73628505 +29095457 +56506740 +56506740 +56506740 +69299029 +69299029 +69299029 +6325660 +6325660 +6325660 +10045474 +10045474 +10045474 +40366667 +40366667 +58053438 +58053438 +58053438 +97045737 +97045737 +97045737 +85888377 +85888377 +95561146 +95561146 +95561146 +59576447 +78114463 +78114463 +#extra +97800311 +97800311 +97800311 +96633955 +96633955 +84815190 +84815190 +26096328 +38354018 +46772449 +29301450 +1528054 +1528054 +98978921 +98978921 +!side diff --git a/Decks/AI_Dogmatika.ydk b/Decks/AI_Dogmatika.ydk index 91e18312..c66e48b2 100644 --- a/Decks/AI_Dogmatika.ydk +++ b/Decks/AI_Dogmatika.ydk @@ -1,59 +1,59 @@ -#created by AlphaKretin +#created by ... #main +51522296 +51522296 +62849088 +69680031 69680031 95679145 -3717252 +72270339 60303688 60303688 60303688 -86120751 -86120751 -86120751 14558127 14558127 14558127 +23434538 +23434538 +23434538 +10158145 +10158145 +10158145 1984618 1984618 1984618 -25311006 -25311006 -25311006 -73628505 -74063034 -74063034 -24224830 +31002402 +60921537 +16240772 24224830 24224830 -48130397 -48130397 -48130397 -47679935 -47679935 -47679935 +65681983 +80845034 +80845034 +80845034 +35569555 +35569555 +35569555 10045474 10045474 10045474 82956214 82956214 82956214 -21011044 -41420027 -41420027 -41420027 #extra -75286621 -20366274 -41209827 -69946549 +24915933 41373230 -97300502 -50907446 -94977269 +11765832 +11765832 +80532587 80532587 80532587 -13529466 +53971455 +53971455 74586817 -98506199 +79606837 +93039339 2220237 +24842059 60303245 -!side +!side \ No newline at end of file diff --git a/Decks/AI_Dragma.ydk b/Decks/AI_Dragma.ydk new file mode 100644 index 00000000..91e18312 --- /dev/null +++ b/Decks/AI_Dragma.ydk @@ -0,0 +1,59 @@ +#created by AlphaKretin +#main +69680031 +95679145 +3717252 +60303688 +60303688 +60303688 +86120751 +86120751 +86120751 +14558127 +14558127 +14558127 +1984618 +1984618 +1984618 +25311006 +25311006 +25311006 +73628505 +74063034 +74063034 +24224830 +24224830 +24224830 +48130397 +48130397 +48130397 +47679935 +47679935 +47679935 +10045474 +10045474 +10045474 +82956214 +82956214 +82956214 +21011044 +41420027 +41420027 +41420027 +#extra +75286621 +20366274 +41209827 +69946549 +41373230 +97300502 +50907446 +94977269 +80532587 +80532587 +13529466 +74586817 +98506199 +2220237 +60303245 +!side diff --git a/Decks/AI_Exosister.ydk b/Decks/AI_Exosister.ydk new file mode 100644 index 00000000..1e5c0fac --- /dev/null +++ b/Decks/AI_Exosister.ydk @@ -0,0 +1,60 @@ +#created by ... +#main +37343995 +37343995 +37343995 +16889337 +16889337 +16889337 +16474916 +16474916 +16474916 +67972302 +67972302 +67972302 +79858629 +79858629 +43863925 +43863925 +43863925 +5352328 +5352328 +14558127 +14558127 +14558127 +23434538 +23434538 +23434538 +84211599 +84211599 +4408198 +24224830 +24224830 +77913594 +77913594 +77913594 +197042 +197042 +10045474 +10045474 +77891946 +77891946 +77891946 +#extra +90448279 +59242457 +59242457 +9272381 +42741437 +42741437 +42741437 +78135071 +78135071 +41524885 +41524885 +46772449 +5530780 +58858807 +8728498 +!side +74689476 diff --git a/Decks/AI_Labrynth.ydk b/Decks/AI_Labrynth.ydk new file mode 100644 index 00000000..d5bcea37 --- /dev/null +++ b/Decks/AI_Labrynth.ydk @@ -0,0 +1,59 @@ +#created by ... +#main +81497285 +81497285 +2347656 +41165831 +73602965 +73602965 +75730490 +1225009 +1225009 +1225009 +37629703 +37629703 +37629703 +14558127 +14558127 +14558127 +23434538 +23434538 +23434538 +74018812 +74018812 +74018812 +2511 +2511 +2511 +49238328 +49238328 +5380979 +5380979 +6351147 +6351147 +10045474 +10045474 +10045474 +30748475 +53417695 +83326048 +92714517 +92714517 +92714517 +#extra +22850702 +22850702 +93039339 +93039339 +29479265 +93084621 +93084621 +24269961 +24269961 +24269961 +67680512 +67680512 +29301450 +71607202 +94259633 +!side diff --git a/Decks/AI_Maliss.ydk b/Decks/AI_Maliss.ydk new file mode 100644 index 00000000..6c185722 --- /dev/null +++ b/Decks/AI_Maliss.ydk @@ -0,0 +1,58 @@ +#created by MDPro3 +#main +68337209 +24224830 +20726052 +23434538 +30118811 +30118811 +75500286 +20938824 +10045474 +24224830 +96676583 +14558127 +27204311 +32061192 +20938824 +40366667 +40366667 +10045474 +68337209 +30118811 +94722358 +40366667 +34267821 +96676583 +3723262 +14558127 +23434538 +32061192 +10045474 +93453053 +69272449 +20938824 +69272449 +65681983 +69272449 +14558127 +32061192 +73628505 +68337209 +96676583 +#extra +39138610 +5043010 +86066372 +4280258 +21848500 +46947713 +95454996 +68059897 +29301450 +59859086 +52698008 +98978921 +30342076 +24842059 +60303245 \ No newline at end of file diff --git a/Decks/AI_MalissOCG.ydk b/Decks/AI_MalissOCG.ydk new file mode 100644 index 00000000..a28077bf --- /dev/null +++ b/Decks/AI_MalissOCG.ydk @@ -0,0 +1,59 @@ +#created by ... +#main +91800273 +91800273 +42141493 +42141493 +42141493 +3723262 +96676583 +96676583 +30118811 +30118811 +69272449 +69272449 +69272449 +32061192 +32061192 +32061192 +20938824 +20938824 +20938824 +14558128 +14558128 +14558128 +23434538 +23434538 +75500286 +24224830 +24224830 +93453053 +68337209 +68337209 +68337209 +10045474 +10045474 +10045474 +20726052 +40366667 +40366667 +40366667 +57111661 +94722358 +#extra +64211118 +39138610 +9940036 +9940036 +92422871 +92422871 +92422871 +21848500 +68059897 +95454996 +46947713 +59859086 +9763474 +52698008 +30342076 +!side diff --git a/Decks/AI_Ryzeal.ydk b/Decks/AI_Ryzeal.ydk new file mode 100644 index 00000000..a7f68416 --- /dev/null +++ b/Decks/AI_Ryzeal.ydk @@ -0,0 +1,74 @@ +#created by ... +#main +8633261 +8633261 +8633261 +35844557 +35844557 +35844557 +72238166 +34022970 +34022970 +34022970 +42141493 +42141493 +84192580 +87126721 +14558127 +14558127 +14558127 +59438930 +59438930 +23434538 +23434538 +94145021 +97268402 +97268402 +97268402 +7477101 +7477101 +7477101 +25311006 +35261759 +35261759 +85106525 +24224830 +24224830 +60394026 +65681983 +6798031 +10045474 +10045474 +10045474 +#extra +9940036 +34909328 +34909328 +2061963 +7511613 +7511613 +45852939 +45852939 +6983839 +90590303 +46772449 +16643334 +21044178 +66011101 +8728498 +!side +20292186 +34267821 +34267821 +34267821 +67173574 +18144507 +19613556 +35269904 +15693423 +83326048 +83326048 +83326048 +41420027 +41420027 +41420027 diff --git a/Decks/AI_SuperheavySamurai.ydk b/Decks/AI_SuperheavySamurai.ydk new file mode 100644 index 00000000..3e9a7841 --- /dev/null +++ b/Decks/AI_SuperheavySamurai.ydk @@ -0,0 +1,59 @@ +#created by ... +#main +83334932 +83334932 +83334932 +82112494 +82112494 +82112494 +19510093 +19510093 +34496660 +34496660 +34496660 +90361010 +90361010 +90361010 +78391364 +78391364 +56727340 +56727340 +14624296 +95500396 +10604644 +23434538 +23434538 +23434538 +14558127 +14558127 +14558127 +49036338 +38814750 +38814750 +97268402 +97268402 +73642296 +73642296 +59438930 +59438930 +59438930 +94145021 +94145021 +94145021 +#extra +64193046 +84815190 +30983281 +44508094 +27548199 +76471944 +74586817 +28912357 +38342335 +27381364 +22423493 +65741786 +33918636 +33918636 +33918636 +!side diff --git a/Decks/AI_Swordsoul.ydk b/Decks/AI_Swordsoul.ydk index d5374fb8..2adb8e2a 100644 --- a/Decks/AI_Swordsoul.ydk +++ b/Decks/AI_Swordsoul.ydk @@ -1,9 +1,8 @@ -#created by wobbaone +#created by ... #main +27204311 87052196 87052196 -87052196 -23431858 23431858 93490856 93490856 @@ -20,39 +19,40 @@ 14558127 14558127 14558127 -98159737 +23434538 +23434538 +23434538 +97268402 +97268402 +97268402 98159737 35261759 35261759 -35261759 56465981 56465981 56465981 -65124425 -65124425 -65124425 93850690 -25789292 -25789292 -25789292 +24224830 +24224830 +65681983 10045474 10045474 10045474 14821890 14821890 #extra -40139997 -96633955 +42632209 +60465049 96633955 84815190 -19048328 +47710198 9464441 5041348 69248256 69248256 83755611 43202238 -73580471 +78917791 32519092 32519092 32519092 diff --git a/Decks/AI_Yubel.ydk b/Decks/AI_Yubel.ydk new file mode 100644 index 00000000..fa7d36ee --- /dev/null +++ b/Decks/AI_Yubel.ydk @@ -0,0 +1,58 @@ +#created by MDPro3 +#main +4779091 +90829280 +90829280 +90829280 +78371393 +78371393 +78371393 +41165831 +60764609 +28803166 +14558127 +14558127 +14558127 +23434538 +23434538 +81034083 +81034083 +81034083 +97651498 +27439792 +24215921 +24215921 +62318994 +62318994 +62318994 +73628505 +65261141 +93729896 +93729896 +93729896 +80312545 +80312545 +80312545 +98567237 +24224830 +24224830 +65681983 +99989863 +80801743 +10045474 +#extra +82135803 +80453041 +93860227 +26096328 +56910167 +70636044 +79559912 +24269961 +67680512 +12067160 +29301450 +29479265 +71818935 +2463794 +60303245 \ No newline at end of file diff --git a/Decks/AI_Zefra.ydk b/Decks/AI_Zefra.ydk new file mode 100644 index 00000000..a8c7aff6 --- /dev/null +++ b/Decks/AI_Zefra.ydk @@ -0,0 +1,79 @@ +#created by ... +#main +49036338 +29432356 +29432356 +29432356 +3611830 +76794549 +5560911 +96227613 +96227613 +27354732 +58990362 +58990362 +58990362 +20773176 +22617205 +69610326 +14785765 +95401059 +31314549 +96223501 +52159691 +21495657 +21495657 +57777714 +92559258 +92559258 +92559258 +38814750 +38814750 +38814750 +72291078 +23434538 +23434538 +23434538 +94693857 +9742784 +19580308 +11609969 +61488417 +2295440 +23581825 +38943357 +38943357 +38943357 +41620959 +41620959 +41620959 +73628505 +74580251 +74580251 +74580251 +81439173 +24224830 +24224830 +46372010 +32354768 +32354768 +32354768 +35561352 +57831349 +#extra +27548199 +74586817 +80696379 +33158448 +65536818 +79606837 +88581108 +96157835 +73347079 +74997493 +44097050 +24094258 +50588353 +36429703 +41999284 +!side diff --git a/ExecutorBase/ExecutorBase.csproj b/ExecutorBase/ExecutorBase.csproj index 4f405212..dbb6c04b 100644 --- a/ExecutorBase/ExecutorBase.csproj +++ b/ExecutorBase/ExecutorBase.csproj @@ -10,7 +10,7 @@ ExecutorBase ExecutorBase v4.0 - 6 + 7 512 true @@ -72,6 +72,7 @@ + diff --git a/ExecutorBase/Game/AI/AIUtil.cs b/ExecutorBase/Game/AI/AIUtil.cs index 3b7943e6..1a00d2ce 100644 --- a/ExecutorBase/Game/AI/AIUtil.cs +++ b/ExecutorBase/Game/AI/AIUtil.cs @@ -442,5 +442,94 @@ public IList CheckSelectCount(IList _selected, IList> GetXyzMaterials(IList param_pre_materials, int level, int material_count, bool material_count_above = false, Func material_func = null) + { + List> result = new List>(); + List pre_materials = param_pre_materials? + .Where(card => card != null && !(card.IsFacedown() && card.Location == CardLocation.MonsterZone) && card.Level == level) + .ToList(); + if (pre_materials == null || pre_materials.Count < material_count) return result; + Func filter = material_func ?? (card => true); + for (int i = 1; i < Math.Pow(2, pre_materials.Count); i++) + { + List temp_materials = new List(); + string binaryString = Convert.ToString(i, 2).PadLeft(pre_materials.Count, '0'); + char[] reversedBinaryChars = binaryString.Reverse().ToArray(); + for (int j = 0; j < pre_materials.Count; j++) + { + if (reversedBinaryChars[j] == '1' && filter(pre_materials[j])) + { + temp_materials.Add(pre_materials[j]); + } + } + if (material_count_above ? temp_materials.Count >= material_count : temp_materials.Count == material_count) + { + result.Add(temp_materials); + } + } + return result; + } + + public List> GetSynchroMaterials(IList param_pre_materials, int level, int tuner_count, int n_tuner_count, bool tuner_count_above = false, bool n_tuner_count_above = true, Func tuner_func = null, Func n_tuner_func = null) + { + List> allByLevel = new List>(); + List pre_materials = param_pre_materials? + .Where(card => card != null && !(card.IsFacedown() && card.Location == CardLocation.MonsterZone) && card.Level > 0) + .ToList(); + if (pre_materials == null || pre_materials.Count < tuner_count + n_tuner_count) return allByLevel; + + Func tunerFilter = tuner_func ?? (card => true); + Func nonTunerFilter = n_tuner_func ?? (card => true); + + pre_materials.Sort(CardContainer.CompareCardLevel); + Stack materials_stack = new Stack(); + for (int i = 0; i < pre_materials.Count; i++) + { + if (pre_materials[i].Level > level) break; + materials_stack.Push(new object[] { i, pre_materials[i].Level, new List { pre_materials[i] } }); + } + + while (materials_stack.Count > 0) + { + object[] data = materials_stack.Pop(); + int index = (int)data[0]; + int sum = (int)data[1]; + List temp_materials = (List)data[2]; + + if (sum == level) + { + allByLevel.Add(temp_materials); + continue; + } + if (sum > level) continue; + + for (int i = index + 1; i < pre_materials.Count; i++) + { + int nextLevel = pre_materials[i].Level; + if (sum + nextLevel > level) break; + var next = new List(temp_materials) { pre_materials[i] }; + materials_stack.Push(new object[] { i, sum + nextLevel, next }); + } + } + + List> result = new List>(); + foreach (List materials in allByLevel) + { + List tunerMaterials = new List(); + List nonTunerMaterials = new List(); + foreach (ClientCard material in materials) + { + if (material.HasType(CardType.Tuner) && tunerFilter(material)) tunerMaterials.Add(material); + else if (!material.HasType(CardType.Tuner) && nonTunerFilter(material)) nonTunerMaterials.Add(material); + } + if ((tuner_count_above ? tunerMaterials.Count >= tuner_count : tunerMaterials.Count == tuner_count) + && (n_tuner_count_above ? nonTunerMaterials.Count >= n_tuner_count : nonTunerMaterials.Count == n_tuner_count)) + { + result.Add(materials); + } + } + return result; + } } } diff --git a/ExecutorBase/Game/AI/CardContainer.cs b/ExecutorBase/Game/AI/CardContainer.cs index 0a09a506..5dd03a61 100644 --- a/ExecutorBase/Game/AI/CardContainer.cs +++ b/ExecutorBase/Game/AI/CardContainer.cs @@ -25,6 +25,15 @@ public static int CompareCardLevel(ClientCard cardA, ClientCard cardB) return 1; } + public static int CompareCardLink(ClientCard cardA, ClientCard cardB) + { + if (cardA.LinkCount < cardB.LinkCount) + return -1; + if (cardA.LinkCount == cardB.LinkCount) + return 0; + return 1; + } + public static int CompareDefensePower(ClientCard cardA, ClientCard cardB) { if (cardA == null && cardB == null) diff --git a/ExecutorBase/Game/AI/DefaultExecutor.cs b/ExecutorBase/Game/AI/DefaultExecutor.cs index 01a545da..19a4be6e 100644 --- a/ExecutorBase/Game/AI/DefaultExecutor.cs +++ b/ExecutorBase/Game/AI/DefaultExecutor.cs @@ -7,6 +7,9 @@ namespace WindBot.Game.AI { public abstract class DefaultExecutor : Executor { + protected readonly List enemyResolvedEffectIdList = new List(); + protected readonly List infiniteImpermanenceNegatedColumns = new List(); + protected class _CardId { public const int JizukirutheStarDestroyingKaiju = 63941210; @@ -94,16 +97,26 @@ protected class _CardId public const int MaxxC = 23434538; public const int LockBird = 94145021; public const int GhostOgreAndSnowRabbit = 59438930; + public const int GhostMournerMoonlitChill = 52038441; public const int GhostBelle = 73642296; public const int EffectVeiler = 97268402; public const int ArtifactLancea = 34267821; + public const int DimensionShifter = 91800273; + public const int MulcharmyFuwalos = 42141493; + public const int MulcharmyNyalus = 87126721; + public const int MulcharmyPurulia = 84192580; public const int CalledByTheGrave = 24224830; + public const int CrossoutDesignator = 65681983; public const int InfiniteImpermanence = 10045474; public const int GalaxySoldier = 46659709; public const int MacroCosmos = 30241314; + public const int DimensionalFissure = 81674782; + public const int BanisheroftheRadiance = 94853057; + public const int BanisheroftheLight = 61528025; public const int UpstartGoblin = 70368879; public const int CyberEmergency = 60600126; + public const int PotOfExtravagance = 84211599; public const int EaterOfMillions = 63845230; @@ -116,7 +129,21 @@ protected class _CardId public const int ImperialOrder = 61740673; public const int RoyalDecreel = 51452091; public const int NaturiaBeast = 33198837; + public const int NaturalExterio = 99916754; public const int AntiSpellFragrance = 58921041; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int SwordsmanLV7 = 37267041; + public const int SkillDrain = 82732705; + public const int KashtiraAriseHeart = 48626373; + public const int DivineArsenalAAZEUS_SkyThunder = 90448279; + public const int MaskedHERODarkLaw = 58481572; + public const int LightningStorm = 14532163; + } + + protected class _Setcode + { + public const int TimeLord = 0x4a; + public const int Danger = 0x11e; } protected DefaultExecutor(GameAI ai, Duel duel) @@ -475,6 +502,26 @@ protected bool DefaultAshBlossomAndJoyousSpring() return false; return Duel.LastChainPlayer == 1; } + + protected bool DefaultCheckWhetherSpellActivateWillBeNegated(ClientCard target) + { + if (target == null) + return false; + if (!(target.IsSpell() || target.IsTrap())) + return false; + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) + return false; + + if (target.IsSpell()) + return DefaultSpellWillBeNegated(); + return DefaultTrapWillBeNegated(); + } + + protected bool DefaultCheckWhetherEnemyCanDraw() + { + // Keep this intentionally simple as a compatibility shim. + return true; + } /// /// Always activate unless the activating card is disabled /// @@ -599,7 +646,7 @@ protected bool DefaultDisableMonster() /// protected bool DefaultSolemnJudgment() { - return !Util.IsChainTargetOnly(Card) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); + return !Util.IsChainTargetOnly(Card) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && !DefaultOnlyHorusSpSummoning() && DefaultTrap(); } /// @@ -607,7 +654,7 @@ protected bool DefaultSolemnJudgment() /// protected bool DefaultSolemnWarning() { - return (Bot.LifePoints > 2000) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); + return (Bot.LifePoints > 2000) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && !DefaultOnlyHorusSpSummoning() && DefaultTrap(); } /// @@ -615,7 +662,7 @@ protected bool DefaultSolemnWarning() /// protected bool DefaultSolemnStrike() { - return (Bot.LifePoints > 1500) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && DefaultTrap(); + return (Bot.LifePoints > 1500) && !(Duel.Player == 0 && Duel.LastChainPlayer == -1) && !DefaultOnlyHorusSpSummoning() && DefaultTrap(); } /// @@ -690,6 +737,11 @@ protected bool DefaultSpellSet() return (Card.IsTrap() || Card.HasType(CardType.QuickPlay) || DefaultSpellMustSetFirst()) && Bot.GetSpellCountWithoutField() < 4; } + protected virtual bool DefaultSetForDiabellze() + { + return DefaultSpellSet(); + } + /// /// Summon with no tribute, or with tributes ATK lower. /// @@ -757,7 +809,8 @@ protected bool DefaultMonsterRepos() /// protected bool DefaultSpellWillBeNegated() { - return (Bot.HasInSpellZone(_CardId.ImperialOrder, true, true) || Enemy.HasInSpellZone(_CardId.ImperialOrder, true)) && !Util.ChainContainsCard(_CardId.ImperialOrder); + return (Bot.HasInSpellZone(_CardId.ImperialOrder, true, true) || Enemy.HasInSpellZone(_CardId.ImperialOrder, true)) && !Util.ChainContainsCard(_CardId.ImperialOrder) + || DefaultCheckWhetherCardIsNegated(Card); } /// @@ -765,7 +818,8 @@ protected bool DefaultSpellWillBeNegated() /// protected bool DefaultTrapWillBeNegated() { - return (Bot.HasInSpellZone(_CardId.RoyalDecreel, true, true) || Enemy.HasInSpellZone(_CardId.RoyalDecreel, true)) && !Util.ChainContainsCard(_CardId.RoyalDecreel); + return (Bot.HasInSpellZone(_CardId.RoyalDecreel, true, true) || Enemy.HasInSpellZone(_CardId.RoyalDecreel, true)) && !Util.ChainContainsCard(_CardId.RoyalDecreel) + || DefaultCheckWhetherCardIsNegated(Card); } /// @@ -806,6 +860,7 @@ protected bool DefaultOnBecomeTarget() /// protected bool DefaultTrap() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return (Duel.LastChainPlayer == -1 && Duel.LastSummonPlayer != 0) || Duel.LastChainPlayer == 1; } @@ -1170,6 +1225,7 @@ protected bool DefaultHonestEffect() { if (Card.Location == CardLocation.Hand) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Bot.BattlingMonster.IsAttack() && (((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Attack) || Bot.BattlingMonster.Attack >= Enemy.LifePoints) || ((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Defense) && (Bot.BattlingMonster.Attack + Enemy.BattlingMonster.Attack > Enemy.BattlingMonster.Defense))); @@ -1177,5 +1233,31 @@ protected bool DefaultHonestEffect() return Util.IsTurn1OrMain2(); } + + protected bool DefaultCheckWhetherCardIsNegated(ClientCard card) + { + if (card == null) return true; + return card.IsDisabled() && card.IsOnField(); + } + + protected bool DefaultCheckWhetherCardIdIsNegated(int cardId) + { + return false; + } + + protected int GetCalledbytheGraveIdCount(int cardId) + { + return 0; + } + + protected bool DefaultOnlyHorusSpSummoning() + { + return false; + } + + protected bool DefaultCheckWhetherBotCanSearch() + { + return true; + } } } diff --git a/ExecutorBase/Game/AI/Enums/DangerousMonster.cs b/ExecutorBase/Game/AI/Enums/DangerousMonster.cs index ffd231c8..034af606 100644 --- a/ExecutorBase/Game/AI/Enums/DangerousMonster.cs +++ b/ExecutorBase/Game/AI/Enums/DangerousMonster.cs @@ -24,6 +24,14 @@ public enum DangerousMonster ZushintheSleepingGiant = 67547370, Heart_eartHDragon = 97403510, DaigustoSphreeze = 29552709, + YubelTheLovingDefenderForever = 47172959, + PhantomOfYubel = 80453041, + UzuhimeTheManifestedMikanko = 57566760, + MaidenInLove = 8445808, + MatadorArchfiend = 7622360, + RedNovaDragonAssaultMode = 82323997, + CrimsonBladeDragon = 3294539 + } } diff --git a/ExecutorBase/Game/AI/Enums/Floodgate.cs b/ExecutorBase/Game/AI/Enums/Floodgate.cs index 9141c503..fc41c93c 100644 --- a/ExecutorBase/Game/AI/Enums/Floodgate.cs +++ b/ExecutorBase/Game/AI/Enums/Floodgate.cs @@ -71,6 +71,7 @@ public enum Floodgate ElShaddollConstruct = 20366274, ElShaddollGrysra = 48424886, ElShaddollWinda = 94977269, + HotRedDragonArchfiendAbyss = 9753964, UltimateConductorTytanno = 18940556, OvertexCoatls = 41782653, FirePrison = 269510, @@ -106,6 +107,204 @@ public enum Floodgate AdamancipatorRisenDragite = 9464441, TeardroptheRikkaQueen = 33779875, CeruleanSkyFire = 54828837, - SacredBeastAwakening = 53701259 + SacredBeastAwakening = 53701259, + GrandSpiritualArtIchirin = 38057522, + DualAvatarFeetArmoredUnGyo = 7631534, + VirtualWorldKyubiShenshen = 92519087, + VirtualWorldGateChuche = 13364097, + DragunityKnightAreadbhair = 88234821, + AiwasstheMagistusSpellSpirit = 35877582, + OneirostheDreamMirrorErlking = 35187185, + PlunderPatrollshipBrann = 94253655, + PlunderPatrollshipMoerk = 20248754, + PlunderPatrollshipLys = 18832779, + HollowGiants = 15462014, + GrozaTyrantofThunder = 45420955, + SpringansCaptainSargas = 29601381, + S_ForceOrrafist = 95974848, + AncientWarriors_RebelliousLuFeng = 82791472, + OneirostheDreamMirrorTormentor = 37678339, + SacredTreeBeastHyperyton = 9349094, + S_ForceJustify = 35334193, + UnderworldGoddessoftheClosedWorld = 98127546, + VampireVoivode = 4918855, + NekrozofAreadbhair = 39468724, + NekrozofUnicore = 89463537, + BenghalancertheResurgent = 73345237, + UrsarcticSeptentrion = 53087962, + TheIrisSwordsoul = 62849088, + AntihumanIntelligenceME_PSY_YA = 58844135, + MagikeyMechmortar_Garesglasser = 45877457, + ShootingMajesticStarDragon = 40939228, + StellarWindWolfrayet = 3322931, + ChronomalyVimana = 2609443, + CyberdarknessDragon = 18967507, + ClearWingSynchroDragon = 82044279, + CrystalClearWingSynchroDragon = 59765225, + BaronnedeFleur = 84815190, + Lyrilusc_EnsemblueRobin = 72971064, + Number4StealthKragen = 67557908, + Floowandereeze_Snowl = 53212882, + Floowandereeze_Empen = 80611581, + MasqueradetheBlazingDragon = 6855503, + DestinyHERO_DestroyerPhoenixEnforcer = 60461804, + SwordsoulGrandmaster_Chixiao = 69248256, + ZoroatheMagistusConflagrantCalamity = 95911373, + MasterflareHyperion = 63101468, + FallenSanctuary = 90312154, + MyutantBeast = 34695290, + MyutantArsenal = 7574904, + MyutantUltimus = 6182103, + Underdog = 5779502, + IcejadeKosmochlor = 3355732, + IllusionofChaos = 12266229, + SwordsoulSinisterSovereign_QixingLongyuan = 47710198, + DDDDeviserKingDeusMachinex = 46593546, + IcejadeErosion = 46593546, + NordicRelicSvalinn = 64797746, + OceanDragonLord_Kairyu_Shin = 23931679, + MarincessAquaArgonaut = 20934852, + MirrorjadetheIcebladeDragon = 44146295, + WitchcrafterVice_Madame = 9603252, + Therion_King_Regulus = 10604644, + StarvingVenomPredapowerFusionDragon = 39915560, + DinomorphiaRexterm = 92798873, + ExosistersMagnifica = 59242457, + IcejadeCurse = 83670388, + MamonakatheVaylantzUnited = 40680521, + LabrynthLabyrinth = 33407125, + RunickFountain = 92107604, + SprightRed = 75922381, + SprightCarrot = 2311090, + RikkaKonkon = 76869711, + PowerToolBraverDragon = 63265554, + MagikeyDeity_Ashtartu = 26988374, + RainbowOverdragon = 37440988, + TheBystialAlbaLos = 69120785, + KashtiraFenrir = 32909498, + MitsutheInsectNinja = 67282505, + VeratheVernusylphGoddess = 55125728, + TearlamentsRulkallos = 84330567, + Black_WingedAssaultDragon = 73218989, + BrandedBeast = 32756828, + GraphaDragonOverlordofDarkWorld = 39552584, + EpurrelyBeauty = 98049934, + ExpurrelyNoir = 83827392, + BeetrooperScaleBomber = 39041550, + EvigishkiNeremanas = 88926295, + ArktosXII_ChronochasmVaylantz = 50687050, + IcejadeGymirAegirine = 86682165, + KashtiraArise_Heart = 48626373, + LaevateinGeneraiderBossofShadows = 74615388, + SynchroZone = 60306277, + BystialDisPater = 27572350, + DespianLuluwalilith = 53971455, + FirewallDragonSingularity = 21637210, + BrandedEtude = 45675980, + EvolzarLars = 35103106, + AltergeistAdminia = 61470213, + EmperorCharlesTheGreat = 97864322, + YouReFinished = 88346805, + VolcanicEmperor = 46412900, + VolcanicInferno = 84138874, + RedZone = 50056656, + TGGlaiveBlaster = 95973569, + StellarNemesisTPHON_DoomsdayStar = 93039339, + SPLittleKnight = 29301450, + AngelicasAngelicRing = 40678060, + SkullGuardianProtectorOfTheVoicelessVoice = 10774240, + FloweringEtoileTheMelodiousMagnificat = 83793721, + EnlightenmentDragon = 46186135, + GoldPrideEradicator = 45464587, + VarudrasTheFinalBringerOfTheEndTimes = 70636044, + RagnaraikaStagSovereign = 42307760, + DrytronMeteonisAlphaDraconids = 56863746, + TheUnstoppableExodiaIncarnate = 83257450, + LightAndDarknessDragonLord = 19652159, + FiendsmithDiesIrae = 82135803, + MementomictlanTecuhtlicaCombinedConqueror = 14529511, + MadolcheQueenTiaraalaFraise = 49689480, + XYZHyperDragonCannon = 75748977, + ClearViciousKnight = 70095046, + Red_EyesBlackFullmetalDragon = 80870883, + MetalzoaX = 27268998, + AzaminaIliaSilvia = 46396218, + HeosvarogTheMechanicalDawn = 8963089, + LegendaryLordSixSamurai_ShiEn = 34235530, + NumberC32SharkDrakeLeVeiss = 7628844, + RyzealDetonator = 34909328, + MalissQHeartsCrypter = 21848500, + Blue_EyesUltimateSpiritDragon = 89604813, + TheiaThePrimalBeing = 85687952, + ArcanaForceEX_TheChaosRuler = 12686296, + SaintAzamina = 85065943, + CrystalClearWingOverSynchroDragon = 84343351, + JurracAstero = 52553102, + NekrozOfMetaltron = 13408726, + ZoroaTheMagistusVerethragna = 37260677, + DiabellstarVengeance = 23151193, + RegenesisWarrior = 96540807, + RegenesisSage = 22938501, + RegenesisDragon = 59323650, + Snake_EyesVengeanceDragon = 79415624, + LegendaryLordSixSamurai_Kizan = 42209438, + JuraishinTheCursedThunderGod = 78693036, + EclipseDragonRulerOfCatastrophes = 30350202, + AlliedCodeTalkerIgnister = 39138610, + EternalSunshine = 28903523, + RegenesisBirth = 27781371, + Ryu_GeEnd = 90664684, + SoulOfGaiaTheFierceKnight = 73129314, + K9_XWerewolf = 90303227, + ArtmageFinmel = 34541940, + LeoWizardTheDarkFiend = 55423549, + LunalightLigerDancer = 54701958, + ArtmageDiactorus = 27184601, + PsychicBlasterMk_II = 88139289, + NumberF0UtopicFutureZexal = 41522092, + VallonTheSuperPsySkyblaster = 40673853, + MasterPeaceTheTrueDracoverlord = 12800564, + MegalithNotrahPlura = 44293356, + AxonKickerOracle = 33171768, + DoomZXIIEnd_Drastrius = 95626382, + DDDSkyKingZeusRagnarok = 30998403, + DarkContractWithTheDifferentDimension = 54936778, + KillerTuneRedSeal = 15665977, + RBGa10Driller = 6043161, + RBGa10Cutter = 33438265, + RBLambdaBlade = 17188206, + K9_XRipperM = 31286915, + TellusionTheMagnaWarrior = 24431911, + ElfnoteLucina = 13597785, + ElfnoteFortuna = 85976588, + BESMetalSlave = 41516133, + DarklordEveningstar = 10136446, + ZalenTheShackledDragon = 4891376, + ExosisterKarmael = 77675029, + BorrelshroudDragon = 66452432, + FluxOchsenfeld = 21861412, + GMXVelox = 82706696, + StardustDragonAssaultMode = 61257789, + StormBaneDragonDestorbim = 94641726, + KewlTuneBack2Back = 65961304, + RedHypernovaDragon = 30698243, + StarvingVenomWingDragon = 5148778, + WeaverofFairyTailTales = 78021082, + ChroniclerofFairyTailTales = 4026187, + SuperdreadnoughtRailCannonGustavRocket = 92359409, + LahamutheMessengerofSacredScripture = 53904087, + Enneapolis = 17621695, + EnneacraftAstaPIXEA = 28454232, + EnneacraftArchaTAIL = 81237046, + EnneacraftAizaLEON = 82359538, + WPFancyBall = 4993187, + StardustDragonVictimSanctuary = 76636978, + DarkMagicianthePharaohsServant = 88570003, + GalaxyEyesPhotonChangeDragon = 54725177, + StarvingVenomFusionDragonoftheFourHeavenlyDragons = 27118421, + CrimsonDragonQuetzacoatl = 29053656, + NeoGalaxyEyesPhotonDragonPhotonHowling = 28331069, + BorreloadLiberatorDragon = 27096833, + PossessedResonanceWynn = 60516416 } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Enums/FusionSpell.cs b/ExecutorBase/Game/AI/Enums/FusionSpell.cs index b4859b55..0526144d 100644 --- a/ExecutorBase/Game/AI/Enums/FusionSpell.cs +++ b/ExecutorBase/Game/AI/Enums/FusionSpell.cs @@ -1,5 +1,8 @@ namespace WindBot.Game.AI.Enums { + /// + /// Spells and monsters that conduct a fusion summon, or have similar effect to fusion summon. + /// public enum FusionSpell { GemKnightFusion = 1264319, @@ -49,11 +52,76 @@ public enum FusionSpell DarkContractwiththeSwampKing = 73360025, NepheShaddollFusion = 60226558, FusionGate = 33550694, - DFusion = 26841274, + FusionDestiny = 52947044, PyroxeneFusion = 55824220, FragmentFusion = 72029628, NecroFusion = 81223446, PredaplantVerteAnaconda = 70369116, + DreamMirrorofChaos = 98570539, + PlunderPatrollShipshapeShipsShipping = 44227727, + FireFormationIngen = 29143457, + ParametalfoesFusion = 58549532, + ReadyFusion = 63854005, + BrandedinWhite = 34995106, + BrandedinRed = 82738008, + FaceCardFusion = 29062925, + MyutantFusion = 42577802, + MyutantCry = 31855260, + FallenOfAlbaz = 68468459, + GreaterPolymerization = 7614732, + UltimateFusion = 71143015, + BrandedFusion = 44362883, + GhostFusion = 35705817, + WitchcrafterConfusionConfession = 35098357, + BrandedBanishment = 6763530, + DinomorphiaDomain = 26631975, + DinomorphiaFrenzy = 78420796, + SouloftheSupremeKing = 92428405, + InstantContact = 16169772, + ScatterFusion = 40597694, + FavoriteContact = 75047173, + AmazonessSecretArts = 86758746, + DarkWorldAccession = 65956182, + BeetrooperLanding = 13234975, + FusionReproduction = 43331750, + ChimeraFusion = 63136489, + HarmonicSynchroFusion = 7473735, + SouloftheSupremeCelestialKing = 76840111, + MelodiousConcerto = 31458630, + MementotlanFusion = 66518509, + TheHallowedAzamina = 94845588, + AzaminaDebtors = 20934683, + AzaminaDetermination = 42201897, + HeavyPolymerization = 58570206, + DoubleFusion = 10218411, + DarkContact = 77124096, + GemKnightMasterDiamondDispersion = 25342956, + GemKnightDispersion = 24220368, + DracotailFaimena = 1498449, + DracotailMululu = 7375867, + KetuDracotail = 6153210, + RahuDracotail = 32548318, + ArtmagePowerPatron = 23829452, + ArtmageMasterworkSuccession = 37517035, + TeleportFusion = 36494597, + PrimiteFusion = 99161253, + TheGazeOfTimaeus = 22283204, + MutinyInTheSky = 71593652, + HuntingHorn = 55421040, + BlazingCartesia = 95515789, + MagnetWarriorSigmaMinus = 87814728, + MagentBonding = 65514302, + ArtmageNonFinito = 74631897, + DDDAlfredTheDivineSageKing = 11852093, + DoubleInterlock = 38129297, + DarklordDance = 99941223, + HecahandsMakibel = 18321034, + PredaplantBaalthea = 66787942, + AstrographSorcerertheStarMagician = 48171151, + OverloadFutureFusion = 33099732, + FutureFusionNova = 92058902, + RevolutionCyberDragon = 66664203, + StarvingVenomFusionDragonoftheFourHeavenlyDragons = 27118421 } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Enums/InvincibleMonster.cs b/ExecutorBase/Game/AI/Enums/InvincibleMonster.cs index 794096b6..71ee2cd9 100644 --- a/ExecutorBase/Game/AI/Enums/InvincibleMonster.cs +++ b/ExecutorBase/Game/AI/Enums/InvincibleMonster.cs @@ -1,7 +1,7 @@ namespace WindBot.Game.AI.Enums { /// - /// Cards that are invincible to battle and should always attack to use effect. + /// Cards that are invincible in battle and should always attack to use effect. /// public enum InvincibleBotMonster { @@ -25,10 +25,17 @@ public enum InvincibleBotMonster DaigustoSphreez = 29552709, Number92HearteartHDragon = 97403510, NumberC96DarkStorm = 77205367, - Number54LionHeart = 54366836 + Number54LionHeart = 54366836, + Number2NinjaShadowMosquito = 32453837, + OhimetheManifestedMikanko = 81260679, + NightmareMagician = 40221691, + ArahimetheManifestedMikanko = 75771170, + UzuhimeTheManifestedMikanko = 57566760, + ArchfiendMatador = 7622360, + CrimsonBladeDragon = 3294539 } /// - /// Cards that are invincible to battle. + /// Cards that are invincible in battle. /// public enum InvincibleEnemyMonster { @@ -93,6 +100,47 @@ public enum InvincibleEnemyMonster GoukiTheGiantOgre = 47946130, BorrelswordDragon = 85289965, NumberF0UtopicFutureDragon = 26973555, - BorrelendDragon = 98630720 + BorrelendDragon = 98630720, + MimickingMan_EaterBug = 72427512, + ShiningPiecephilia = 49776811, + BrigrandtheGloryDragon = 34848821, + AmazonessQueen = 15951532, + Number2NinjaShadowMosquito = 32453837, + OhimetheManifestedMikanko = 81260679, + CornfieldCoatl = 92565383, + MirrorSwordknight = 28954097, + NightmareMagician = 40221691, + ArahimetheManifestedMikanko = 75771170, + UFOLight = 9275482, + TaotheGreatChanter = 34541543, + SpiritOfYubel = 90829280, + DarkGuardian = 26746975, + EnvoyOfTheWaxState = 87462901, + Fluffyfluff = 85401123, + YubelDasEwigLiebeWachter = 47172959, + TenpaiDragonFadra = 65326118, + GolgoilTheSteelSeismicSmasher = 59369430, + TalonsOfShurilane = 74150658, + NightmareApprentice = 58143852, + MillenniumoonMaiden = 37552929, + MillenniumFiendReflection = 63947968, + HereticalPhoboscobos = 76078185, + TopHatHareTheSilhouetteMagician = 1528054, + Silhouhatte = 24440742, + TheDukeofDemise = 45445571, + EvilHERONeosLord = 13708888, + GladiatorBeastDareios = 72246674, + Number69HeraldryCrest_DarkMatterDemolition = 77571454, + FirstOfTheDragonlords = 53466722, + UzuhimeTheManifestedMikanko = 57566760, + HecahandsIbel = 95365081, + HecahandsYadel = 32759190, + HecahandsGoddus = 68144894, + HecahandsGygas = 95132593, + HecahandsBreus = 21637502, + HecahandsJawza = 67021206, + HecahandsXeno = 94410955, + MaidenInLove = 8445808, + ArchfiendMatador = 7622360 } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Enums/OneForXyz.cs b/ExecutorBase/Game/AI/Enums/OneForXyz.cs index 074fae2a..2627e307 100644 --- a/ExecutorBase/Game/AI/Enums/OneForXyz.cs +++ b/ExecutorBase/Game/AI/Enums/OneForXyz.cs @@ -1,5 +1,8 @@ namespace WindBot.Game.AI.Enums { + /// + /// Monsters that can be used as the sole material for an XYZ summon + /// public enum OneForXyz { ZoodiacThoroughblade = 77150143, @@ -18,6 +21,12 @@ public enum OneForXyz GalaxyEyesCipherDragon = 18963306, Number107 = 88177324, CyberDragonNova = 58069384, - Number39 = 84013237 + Number39 = 84013237, + Argostars_GloriousAdra = 91438674, + Argostars_LightningTydeu = 65889305, + Argostars_SwiftCapane = 91284003, + Argostars_SlayerEteo = 38379052, + Argostars_FierceParthe = 21050476 + //HeavyKnightBabelDecker = 45116390 //Unsure if this should be included as its conditional } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Enums/PreventActivationEffectInBattle.cs b/ExecutorBase/Game/AI/Enums/PreventActivationEffectInBattle.cs index 34f98e6d..c570a189 100644 --- a/ExecutorBase/Game/AI/Enums/PreventActivationEffectInBattle.cs +++ b/ExecutorBase/Game/AI/Enums/PreventActivationEffectInBattle.cs @@ -1,5 +1,8 @@ namespace WindBot.Game.AI.Enums { + /// + /// Monsters that prevent an opponent from using effects on attack declaration or in the damage step + /// public enum PreventActivationEffectInBattle { Deskbot009 = 25494711, @@ -11,5 +14,8 @@ public enum PreventActivationEffectInBattle SamuraiDestroyer = 40509732, ArmadesKeeperOfBoundaries = 88033975, NumberS39UtopiaTheLightning = 56832966, + DualAvatarEmpoweredKonGyo = 33026283, + GoukiTheTyrantOgre = 7782069, + SangenpaiTranscendentDragion = 18969888 //Technically stopping any effects in BP counts } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs b/ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs index 547e0c16..8a6fb7d8 100644 --- a/ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs +++ b/ExecutorBase/Game/AI/Enums/ShouldBeDisabledBeforeItUseEffectMonster.cs @@ -51,11 +51,27 @@ public enum ShouldBeDisabledBeforeItUseEffectMonster RedFamiliar = 8372133, AccesscodeTalker = 86066372, ChaosSummoningBeast = 27439792, - CosmoBrain = 85679527, ShiranuiSolitaire = 94801854, Mixeroid = 71340250, LonefireBlossom = 48686504, - BrotherhoodoftheFireFist_Leopard = 39699564 + BrotherhoodoftheFireFist_Leopard = 39699564, + SpringansPedor = 56818977, + GizmekNaganakitheSunriseSignaler = 96399967, + MyutantMutant = 26561172, + ThreeEyedGhost = 31464658, + UrsarcticPolarStar = 62714453, + BattlinBoxerPromoter = 83315222, + TGRocketSalamander = 77392987, + GGolemRockHammer = 98875863, + WishDragon = 64583600, + ChaosWitch = 30327674, + FissioningMotherSpider = 34034150, + AncientGearStatue = 32762201, + SplittingPlanarian = 44175358, + TestBear = 62076252, + RescueHedgehog = 99742859, + SuperQuantalFairyZetan = 66646087, + LightWaterDragon = 82616239 } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs index 855824e2..885122ad 100644 --- a/ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs +++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeMonsterTarget.cs @@ -1,8 +1,8 @@ -namespace WindBot.Game.AI.Enums +namespace WindBot.Game.AI.Enums { /// - /// Cards that are can't be selected as target of monster's effect, or immuned to monster's effect. - /// So them shouldn't be tried to be selected as target of monster at most times. + /// Cards that are can't be selected as target of monster's effect, or immune to monster's effect. + /// So they shouldn't be selected as target of monster at most times. /// public enum ShouldNotBeMonsterTarget { @@ -13,7 +13,16 @@ public enum ShouldNotBeMonsterTarget CXyzSimontheGreatMoralLeader = 41147577, PaleozoicAnomalocaris = 61307542, PaleozoicOpabinia = 37649320, + PaleozoicCambroraster = 36346532, BorreloadDragon = 31833038, - BorrelendDragon = 98630720 + BorrelendDragon = 98630720, + DrytronMeteonisDraconids = 69815951, + MyutantBeast = 34695290, + VeidosTheDragonOfEndlessDarkness = 8540986, + FirstOfTheDragonlords = 53466722, + JunkWarriorAssaultMode = 23219323, + ArchfiendEmperor = 48469380, + DoomReginaArchfiend = 78744660, + PerfectronHydradriveDragon = 13203964 } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs index fa78eaf9..94517b8d 100644 --- a/ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs +++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeSpellTarget.cs @@ -1,14 +1,17 @@ namespace WindBot.Game.AI.Enums { /// - /// Cards that are can't be selected as target of spell&trap's effect, or immuned to spell&trap's effect. - /// So them shouldn't be tried to be selected as target of spell&trap at most times. + /// Cards that are can't be selected as target of spell&trap's effect, or immune to spell&trap's effect. + /// So they shouldn't be selected as target of spell&trap at most times. /// public enum ShouldNotBeSpellTrapTarget { ApoqliphortTowers = 27279764, ApoqliphortSkybase = 40061558, TheLegendaryFishermanIII = 44968687, - ChaosAncientGearGiant = 51788412 + ChaosAncientGearGiant = 51788412, + DrytronMeteonisQuadrantids = 95209656, + QliphortGenius = 22423493, + ElShaddollMeshahrail = 32467459 } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs b/ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs index c3e066d7..6306d604 100644 --- a/ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs +++ b/ExecutorBase/Game/AI/Enums/ShouldNotBeTarget.cs @@ -1,8 +1,8 @@ namespace WindBot.Game.AI.Enums { /// - /// Cards that are can't be selected as target, or immuned to most effect. - /// So them shouldn't be tried to be selected as target at most times. + /// Cards that can't be selected as target, or immune to most effects. + /// So they shouldn't be selected as target most times. /// public enum ShouldNotBeTarget { @@ -49,6 +49,16 @@ public enum ShouldNotBeTarget Blackwing_FullArmoredWing = 54082269, DragunofRedEyes = 37818794, RedEyesBDragon = 74677422, // sometimes the name of DragunofRedEyes will be changed to RedEyesBDragon - TheArrivalCyberseIgnister = 11738489 + TheArrivalCyberseIgnister = 11738489, + MajespecterPorcupineYamaarashi = 51073802, + RaidraptorRisingRebellionFalcon = 71222868, + Number97Draglubion = 28400508, + SaintAzamina = 85065943, + LunalightLigerDancer = 54701958, + DarklordEveningstar = 10136446, + TheFirstDarklord = 4167084, + StormBaneDragonDestorbim = 94641726, + PowerPatronVidrium = 70488851, + RedHypernovaDragon = 30698243 } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/Executor.cs b/ExecutorBase/Game/AI/Executor.cs index 7b8b36f8..9e4a6d6e 100644 --- a/ExecutorBase/Game/AI/Executor.cs +++ b/ExecutorBase/Game/AI/Executor.cs @@ -22,6 +22,7 @@ public abstract class Executor protected ExecutorType Type { get; private set; } protected ClientCard Card { get; private set; } protected long ActivateDescription { get; private set; } + protected int CurrentTiming { get; private set; } protected ClientField Bot { get; private set; } protected ClientField Enemy { get; private set; } @@ -97,10 +98,41 @@ public virtual void OnChaining(int player, ClientCard card) // For overriding } + public virtual void OnChainSolved(int chainIndex) + { + // For overriding + } + public virtual void OnChainEnd() { // For overriding } + + public virtual void OnReceivingAnnouce(int player, int data) + { + // For overriding + } + + public virtual void OnPlayerHint(int player, int hintType, int description) + { + // For overriding + } + + public virtual void OnHintZone(int player, int zone) + { + // For overriding + } + + public virtual void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + // For overriding + } + + public virtual void OnSpSummoned() + { + // For overriding + } + public virtual void OnNewPhase() { // Some AI need do something on new phase @@ -236,11 +268,12 @@ public void SetBattle(BattlePhase battle) /// /// Set global variables Type, Card, ActivateDescription for Executor /// - public void SetCard(ExecutorType type, ClientCard card, long description) + public void SetCard(ExecutorType type, ClientCard card, long description, int timing = -1) { Type = type; Card = card; ActivateDescription = description; + CurrentTiming = timing; } /// @@ -280,4 +313,4 @@ private bool DefaultNoExecutor() return Executors.All(exec => exec.Type != Type || exec.CardId != Card.Id); } } -} +} \ No newline at end of file diff --git a/ExecutorBase/Game/AI/HintMsg.cs b/ExecutorBase/Game/AI/HintMsg.cs index 98e723a4..a781df42 100644 --- a/ExecutorBase/Game/AI/HintMsg.cs +++ b/ExecutorBase/Game/AI/HintMsg.cs @@ -56,6 +56,13 @@ public static class HintMsg DisableZone = 570, ToZone = 571, Counter = 572, - Negate = 575; + Disable = 573, + OperateCard = 574, + Negate = 575, + RITUAL = 1057, + FUSION = 1056, + SYNCHRO = 1063, + XYZ = 1073, + PENDULUM = 1074; } } diff --git a/ExecutorBase/Game/ChainInfo.cs b/ExecutorBase/Game/ChainInfo.cs new file mode 100644 index 00000000..b8c1aac3 --- /dev/null +++ b/ExecutorBase/Game/ChainInfo.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game +{ + public class ChainInfo + { + public ClientCard RelatedCard { get; private set; } + public int ActivatePlayer { get; private set; } + public int ActivateId { get; private set; } + public int ActivateAlias { get; private set; } + public int ActivateController { get; private set; } + public int ActivatePosition { get; private set; } + public int ActivateSequence { get; private set; } + public CardLocation ActivateLocation { get; private set; } + public int ActivateLevel { get; private set; } + public int ActivateRank { get; private set; } + public ulong ActivateType { get; private set; } + public ulong ActivateRace { get; private set; } + public ulong ActivateAttack { get; private set; } + public ulong ActivateDefense { get; private set; } + public bool IsSpecialSummoned { get; private set; } + public long ActivateDescription { get; private set; } + + public ChainInfo(ClientCard card) + : this(card, card.Controller, 0) + { + } + + public ChainInfo(ClientCard card, int player, long desc) + { + RelatedCard = card; + ActivatePlayer = player; + ActivateId = card.Id; + ActivateAlias = card.Alias; + ActivateController = card.Controller; + ActivatePosition = card.Position; + ActivateSequence = card.Sequence; + ActivateLocation = card.Location; + ActivateLevel = card.Level; + ActivateRank = card.Rank; + ActivateType = (ulong)card.Type; + ActivateRace = card.Race; + ActivateAttack = (ulong)card.Attack; + + ActivateDefense = (ulong)card.Defense; + IsSpecialSummoned = card.IsSpecialSummoned; + ActivateDescription = desc; + } + + public bool HasPosition(CardPosition position) + { + return (ActivatePosition & (int)position) != 0; + } + + public bool HasLocation(CardLocation location) + { + return ((int)ActivateLocation & (int)location) != 0; + } + + public bool IsActivateCode(int id) + { + return ActivateId == id || Math.Abs(ActivateAlias - ActivateId) <= 20 && ActivateAlias == id; + } + + public bool IsCode(long id) + { + return RelatedCard != null && RelatedCard.IsCode(id); + } + + public bool IsCode(IList ids) + { + return RelatedCard != null && RelatedCard.IsCode(ids); + } + + public bool IsCode(params int[] ids) + { + return RelatedCard != null && RelatedCard.IsCode(ids); + } + + public bool HasType(CardType type) + { + return RelatedCard != null && (RelatedCard.Type & (int)type) != 0; + } + + public bool IsSpell() + { + return HasType(CardType.Spell); + } + + public bool IsTrap() + { + return HasType(CardType.Trap); + } + } +} diff --git a/ExecutorBase/Game/ClientCard.cs b/ExecutorBase/Game/ClientCard.cs index 2dc0b23a..13957ecb 100644 --- a/ExecutorBase/Game/ClientCard.cs +++ b/ExecutorBase/Game/ClientCard.cs @@ -370,7 +370,7 @@ public bool IsCanRevive() return ProcCompleted != 0 || !(IsExtraCard() || HasType(CardType.Ritual) || HasType(CardType.SpSummon)); } - public bool IsCode(int id) + public bool IsCode(long id) { return Id == id || Alias != 0 && Alias == id; } @@ -390,6 +390,11 @@ public bool IsOriginalCode(int id) return Id == id || Alias - Id < 10 && Alias == id; } + public bool IsOnField() + { + return Location == CardLocation.MonsterZone || Location == CardLocation.SpellZone || Location == CardLocation.PendulumZone || Location == CardLocation.FieldZone; + } + public bool HasXyzMaterial() { return Overlays.Count > 0; @@ -410,6 +415,17 @@ public int GetDefensePower() return IsAttack() ? Attack : Defense; } + public int GetOriginCode() + { + int code = Id; + if (Data != null) + { + if (Data.Alias > 0) code = Data.Alias; + else code = Data.Id; + } + return code; + } + public bool Equals(ClientCard card) { return ReferenceEquals(this, card); diff --git a/ExecutorBase/Game/ClientField.cs b/ExecutorBase/Game/ClientField.cs index aa596f89..50ad27f0 100644 --- a/ExecutorBase/Game/ClientField.cs +++ b/ExecutorBase/Game/ClientField.cs @@ -18,6 +18,7 @@ public class ClientField public int LifePoints; public ClientCard BattlingMonster; public bool UnderAttack; + public HashSet HintDescriptions { get; private set; } public ClientField() { @@ -32,6 +33,7 @@ public void Init(int deck, int extra) Banished = new List(); Deck = new List(); ExtraDeck = new List(); + HintDescriptions = new HashSet(); for (int i = 0; i < deck; ++i) Deck.Add(new ClientCard(0, CardLocation.Deck, -1, 0)); @@ -46,6 +48,7 @@ public void Clear() Hand.Clear(); Banished.Clear(); Graveyard.Clear(); + HintDescriptions.Clear(); MonsterZone = new ClientCard[7]; SpellZone = new ClientCard[8]; } diff --git a/ExecutorBase/Game/Dialogs.cs b/ExecutorBase/Game/Dialogs.cs index 7bd9360f..05ee06b5 100644 --- a/ExecutorBase/Game/Dialogs.cs +++ b/ExecutorBase/Game/Dialogs.cs @@ -35,6 +35,8 @@ public class DialogsData public string[] setmonster { get; set; } [DataMember] public string[] chaining { get; set; } + [DataMember] + public string[] custom { get; set; } } public class Dialogs { @@ -52,6 +54,7 @@ public class Dialogs private string[] _summon; private string[] _setmonster; private string[] _chaining; + private string[] _custom; private Action Chat; @@ -77,6 +80,7 @@ public Dialogs(string dialogfilename, Action chat, string path) _summon = data.summon; _setmonster = data.setmonster; _chaining = data.chaining; + _custom = data.custom; } } @@ -160,6 +164,20 @@ public void SendChaining(string card) InternalSendMessage(_chaining, card); } + public void SendCustomChat(int index, params object[] opts) + { + if (_custom == null || index < 0 || index >= _custom.Length) + return; + + string pattern = _custom[index]; + if (string.IsNullOrEmpty(pattern)) + return; + + string message = string.Format(pattern, opts); + if (message != "") + Chat(message, false); + } + private void InternalSendMessage(IList array, params object[] opts) { string message = string.Format(array[Rand.Next(array.Count)], opts); diff --git a/ExecutorBase/Game/Duel.cs b/ExecutorBase/Game/Duel.cs index 8a4ccaeb..6530d6a2 100644 --- a/ExecutorBase/Game/Duel.cs +++ b/ExecutorBase/Game/Duel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using YGOSharp.OCGWrapper.Enums; namespace WindBot.Game @@ -19,11 +20,16 @@ public class Duel public int LastChainPlayer { get; set; } public IList CurrentChain { get; set; } + public IList CurrentChainInfo { get; set; } public IList ChainTargets { get; set; } + public IList LastChainTargets { get; set; } public IList ChainTargetOnly { get; set; } + public CardLocation LastChainLocation { get; set; } + public List NegatedChainIndexList { get; set; } public int LastSummonPlayer { get; set; } public IList SummoningCards { get; set; } public IList LastSummonedCards { get; set; } + public int SolvingChainIndex { get; set; } public bool MainPhaseEnd { get; set; } public Duel() @@ -34,11 +40,16 @@ public Duel() LastChainPlayer = -1; MainPhaseEnd = false; CurrentChain = new List(); + CurrentChainInfo = new List(); ChainTargets = new List(); + LastChainTargets = new List(); ChainTargetOnly = new List(); + LastChainLocation = 0; + NegatedChainIndexList = new List(); LastSummonPlayer = -1; SummoningCards = new List(); LastSummonedCards = new List(); + SolvingChainIndex = 0; } public void Clear() @@ -48,8 +59,12 @@ public void Clear() LastChainPlayer = -1; MainPhaseEnd = false; CurrentChain.Clear(); + CurrentChainInfo.Clear(); ChainTargets.Clear(); + LastChainTargets.Clear(); ChainTargetOnly.Clear(); + LastChainLocation = 0; + NegatedChainIndexList.Clear(); LastSummonPlayer = -1; SummoningCards.Clear(); LastSummonedCards.Clear(); @@ -203,5 +218,22 @@ public int GetLocalPlayer(int player) { return IsFirst ? player : 1 - player; } + + public ClientCard GetCurrentSolvingChainCard() + { + if (SolvingChainIndex == 0 || SolvingChainIndex > CurrentChain.Count) return null; + return CurrentChain[SolvingChainIndex - 1]; + } + + public ChainInfo GetCurrentSolvingChainInfo() + { + if (SolvingChainIndex == 0 || SolvingChainIndex > CurrentChainInfo.Count) return null; + return CurrentChainInfo[SolvingChainIndex - 1]; + } + + public bool IsCurrentSolvingChainNegated() + { + return SolvingChainIndex > 0 && NegatedChainIndexList.Contains(SolvingChainIndex); + } } } diff --git a/ExecutorBase/Game/GameAI.cs b/ExecutorBase/Game/GameAI.cs index 267d8def..c489ff93 100644 --- a/ExecutorBase/Game/GameAI.cs +++ b/ExecutorBase/Game/GameAI.cs @@ -64,6 +64,11 @@ public void OnStart() _dialogs.SendDuelStart(); } + public void SendCustomChat(int index, params object[] opts) + { + _dialogs.SendCustomChat(index, opts); + } + /// /// Called when the AI do the rock-paper-scissors. /// @@ -137,6 +142,11 @@ public void OnChaining(ClientCard card, int player) { Executor.OnChaining(player,card); } + + public void OnChainSolved(int chainIndex) + { + Executor.OnChainSolved(chainIndex); + } /// /// Called when a chain has been solved. diff --git a/ExecutorBase/YGOSharp.OCGWrapper.Enums/CardRace.cs b/ExecutorBase/YGOSharp.OCGWrapper.Enums/CardRace.cs index 0a8bef5b..cf46ecb5 100644 --- a/ExecutorBase/YGOSharp.OCGWrapper.Enums/CardRace.cs +++ b/ExecutorBase/YGOSharp.OCGWrapper.Enums/CardRace.cs @@ -17,7 +17,7 @@ public enum CardRace : ulong Thunder = 0x1000, Dragon = 0x2000, Beast = 0x4000, - BestWarrior = 0x8000, + BeastWarrior = 0x8000, Dinosaur = 0x10000, Fish = 0x20000, SeaSerpent = 0x40000, diff --git a/ExecutorBase/YGOSharp.OCGWrapper/NamedCard.cs b/ExecutorBase/YGOSharp.OCGWrapper/NamedCard.cs index effc4983..c5f8086d 100644 --- a/ExecutorBase/YGOSharp.OCGWrapper/NamedCard.cs +++ b/ExecutorBase/YGOSharp.OCGWrapper/NamedCard.cs @@ -1,4 +1,4 @@ -using System.Data; +using System.Data; namespace YGOSharp.OCGWrapper { @@ -13,7 +13,7 @@ internal NamedCard(IDataRecord reader) : base(reader) Description = reader.GetString(11); } - public static new NamedCard Get(int id) + public static new NamedCard Get(long id) { return NamedCardsManager.GetCard(id); } diff --git a/ExecutorBase/YGOSharp.OCGWrapper/NamedCardsManager.cs b/ExecutorBase/YGOSharp.OCGWrapper/NamedCardsManager.cs index d4fa5873..203386ce 100644 --- a/ExecutorBase/YGOSharp.OCGWrapper/NamedCardsManager.cs +++ b/ExecutorBase/YGOSharp.OCGWrapper/NamedCardsManager.cs @@ -10,11 +10,11 @@ namespace YGOSharp.OCGWrapper { public static class NamedCardsManager { - private static IDictionary _cards = new Dictionary(); + private static IDictionary _cards = new Dictionary(); public static void SetThreadSafe() { - _cards = new ConcurrentDictionary(); + _cards = new ConcurrentDictionary(); } public static void LoadDatabase(string databaseFullPath) @@ -51,7 +51,7 @@ public static void LoadDatabase(string databaseFullPath) } } - internal static NamedCard GetCard(int id) + internal static NamedCard GetCard(long id) { if (_cards.ContainsKey(id)) return _cards[id]; diff --git a/Game/AI/Decks/AlbazExecutor.cs b/Game/AI/Decks/AlbazExecutor.cs new file mode 100644 index 00000000..fe42f8b0 --- /dev/null +++ b/Game/AI/Decks/AlbazExecutor.cs @@ -0,0 +1,5861 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using System; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Albaz", "AI_Albaz")] + public class AlbazExecutor : DefaultExecutor + { + public class CardId + { + public const int TheBystialLubellion = 32731036; + public const int AlbionTheShroudedDragon = 25451383; + public const int BystialSaronir = 60242223; + public const int AluberTheJesterOfDespia = 62962630; + public const int FallenOfAlbaz = 68468459; + public const int SpringansKitt = 45484331; + public const int GuidingQuemTheVirtuous = 45883110; + public const int BlazingCartesiaTheVirtuous = 95515789; + public const int TriBrigadeMercourier = 19096726; + // _CardId.AshBlossom = 14558127; + // _CardId.MaxxC = 23434538; + public const int DespianTragedy = 36577931; + + public const int NadirServant = 1984618; + public const int FusionDeployment = 6498706; + public const int BrandedInWhite = 34995106; + public const int BrandedFusion = 44362883; + public const int GoldSarcophagus = 75500286; + public const int FoolishBurial = 81439173; + // _CardId.CalledByTheGrave = 24224830; + public const int BrandedInHighSpirits = 29948294; + public const int BrandedOpening = 36637374; + // _CardId.CrossoutDesignator = 65681983; + public const int BrandedInRed = 82738008; + public const int BrandedLost = 18973184; + + // _CardId.InfiniteImpermanence = 10045474; + public const int BrightestBlazingBrandedKing = 19271881; + public const int BrandedBeast = 32756828; + public const int BrandedRetribution = 17751597; + + public const int GuardianChimera = 11321089; + public const int AlbionTheSanctifireDragon = 38811586; + public const int MirrorjadeTheIcebladeDragon = 44146295; + public const int BorreloadFuriousDragon = 92892239; + public const int LubellionTheSearingDragon = 70534340; + public const int AlbaLenatusTheAbyssDragon = 3410461; + public const int GranguignolTheDuskDragon = 24915933; + public const int DespianQuaeritis = 72272462; + public const int SprindTheIrondashDragon = 1906812; + public const int TitanikladTheAshDragon = 41373230; + public const int RindbrummTheStrikingDragon = 51409648; + public const int AlbionTheBrandedDragon = 87746184; + public const int DespianLuluwalilith = 53971455; + + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + public const int SkillDrain = 82732705; + + public const int DimensionShifter = 91800273; + public const int MacroCosmos = 30241314; + public const int DimensionalFissure = 81674782; + public const int BanisheroftheRadiance = 94853057; + public const int BanisheroftheLight = 61528025; + public const int KashtiraAriseHeart = 48626373; + public const int AccesscodeTalker = 86066372; + public const int GhostMournerMoonlitChill = 52038441; + public const int NibiruThePrimalBeing = 27204311; + } + + public AlbazExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + + // + AddExecutor(ExecutorType.Summon, AdvanceSummon); + AddExecutor(ExecutorType.SpSummon, CardId.AlbaLenatusTheAbyssDragon, AlbaLenatusTheAbyssDragonSpSummon); + AddExecutor(ExecutorType.MonsterSet, CardId.FallenOfAlbaz, FallenOfAlbazSet); + AddExecutor(ExecutorType.Activate, CardId.BlazingCartesiaTheVirtuous, BlazingCartesiaTheVirtuousActivateInGrave); + + // quick effect + AddExecutor(ExecutorType.Activate, CardId.BrandedRetribution, BrandedRetributionActivate); + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, _CardId.CrossoutDesignator, CrossoutDesignatorActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, CardId.BrandedBeast, BrandedBeastActivate); + AddExecutor(ExecutorType.Activate, CardId.BrightestBlazingBrandedKing, BrightestBlazingBrandedKingActivate); + AddExecutor(ExecutorType.Activate, CardId.BrandedOpening, BrandedOpeningActivate); + AddExecutor(ExecutorType.Activate, CardId.BrandedInHighSpirits, BrandedInHighSpiritsActivate); + AddExecutor(ExecutorType.Activate, CardId.RindbrummTheStrikingDragon, RindbrummTheStrikingDragonActivate); + + // remove + AddExecutor(ExecutorType.Activate, CardId.GuardianChimera, GuardianChimeraActivate); + AddExecutor(ExecutorType.Activate, CardId.BorreloadFuriousDragon, BorreloadFuriousDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.TriBrigadeMercourier, TriBrigadeMercourierActivate); + AddExecutor(ExecutorType.Activate, CardId.DespianQuaeritis, DespianQuaeritisActivate); + AddExecutor(ExecutorType.Activate, CardId.MirrorjadeTheIcebladeDragon, MirrorjadeTheIcebladeDragonActivate); + + // search + AddExecutor(ExecutorType.Activate, CardId.TheBystialLubellion, TheBystialLubellionActivate); + AddExecutor(ExecutorType.SpSummon, CardId.TheBystialLubellion, TheBystialLubellionSpSummon); + AddExecutor(ExecutorType.Activate, CardId.FoolishBurial, FoolishBurialActivate); + AddExecutor(ExecutorType.Activate, CardId.GoldSarcophagus, GoldSarcophagusActivate); + AddExecutor(ExecutorType.Activate, CardId.AluberTheJesterOfDespia, AluberTheJesterOfDespiaActivate); + + AddExecutor(ExecutorType.Activate, CardId.AlbionTheSanctifireDragon, AlbionTheSanctifireDragonActivate); + + // blazing + AddExecutor(ExecutorType.Activate, CardId.FusionDeployment, FusionDeploymentActivate); + + AddExecutor(ExecutorType.Activate, CardId.AlbionTheShroudedDragon, AlbionTheShroudedDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.BystialSaronir, BystialSaronirActivate); + + // summon for search + AddExecutor(ExecutorType.Summon, CardId.AluberTheJesterOfDespia, AluberTheJesterOfDespiaSummon); + AddExecutor(ExecutorType.Summon, CardId.GuidingQuemTheVirtuous, GuidingQuemTheVirtuousSummonForSearch); + AddExecutor(ExecutorType.Activate, CardId.SpringansKitt, SpringansKittActivate); + AddExecutor(ExecutorType.Summon, CardId.SpringansKitt, SpringansKittSummon); + + // fusion & lost + AddExecutor(ExecutorType.Activate, CardId.BrandedLost, BrandedLostCardActivate); + AddExecutor(ExecutorType.Activate, CardId.GranguignolTheDuskDragon, GranguignolTheDuskDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.AlbaLenatusTheAbyssDragon, AlbaLenatusTheAbyssDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.SprindTheIrondashDragon, SprindTheIrondashDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.TitanikladTheAshDragon, TitanikladTheAshDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.AlbionTheBrandedDragon, AlbionTheBrandedDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.LubellionTheSearingDragon, LubellionTheSearingDragonActivate); + AddExecutor(ExecutorType.Summon, CardId.BlazingCartesiaTheVirtuous, BlazingCartesiaTheVirtuousSummon); + AddExecutor(ExecutorType.Activate, CardId.FallenOfAlbaz, FallenOfAlbazActivate); + AddExecutor(ExecutorType.Activate, CardId.BrandedFusion, BrandedFusionActivate); + AddExecutor(ExecutorType.Activate, CardId.BlazingCartesiaTheVirtuous, BlazingCartesiaTheVirtuousActivate); + AddExecutor(ExecutorType.Activate, CardId.BrandedInWhite, BrandedInWhiteActivate); + AddExecutor(ExecutorType.Activate, CardId.BrandedInRed, BrandedInRedActivate); + AddExecutor(ExecutorType.Activate, CardId.BrandedLost, BrandedLostActivate); + + // albaz summon + AddExecutor(ExecutorType.Summon, CardId.FallenOfAlbaz, FallenOfAlbazSummon); + AddExecutor(ExecutorType.Summon, CardId.GuidingQuemTheVirtuous, GuidingQuemTheVirtuousSummon); + + // delay + AddExecutor(ExecutorType.Activate, CardId.DespianTragedy, DespianTragedyActivate); + AddExecutor(ExecutorType.Activate, CardId.TriBrigadeMercourier, TriBrigadeMercourierActivateForSearch); + AddExecutor(ExecutorType.Activate, CardId.NadirServant, NadirServantActivate); + + AddExecutor(ExecutorType.MonsterSet, SetForChimera); + AddExecutor(ExecutorType.MonsterSet, CardId.DespianTragedy, DespianTragedySet); + AddExecutor(ExecutorType.Repos, DefaultMonsterRepos); + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + AddExecutor(ExecutorType.Activate, CardId.GuidingQuemTheVirtuous, GuidingQuemTheVirtuousActivate); + AddExecutor(ExecutorType.Activate, CardId.DespianLuluwalilith, DespianLuluwalilithActivate); + + AddExecutor(ExecutorType.Activate, FloogateActivate); + + } + + const int SetcodeTimeLord = 0x4a; + const int SetcodePhantom = 0xdb; + const int SetcodeOrcust = 0x11b; + const int SetcodeBranded = 0x160; + const int SetcodeDespain = 0x166; + const int SetcodeBystial = 0x189; + const int SetcodeHorus = 0x3; + const int hintTimingMainEnd = 0x4; + const int hintBattleStart = 0x8; + List notToNegateIdList = new List{ + 58699500, 20343502, CardId.AlbionTheShroudedDragon, 19403423 + }; + List cannotBeFusionMaterialIdList = new List + { + CardId.AlbaLenatusTheAbyssDragon, CardId.AlbionTheSanctifireDragon, 79229522, 65029288, 30864377, 33964637, 87116928, 13735899, 28226490, 80453041, + _CardId.EaterOfMillions, 2992467, 16366810, 40217358, 47346782, 50893987, 71459017, 84433295, 85101097 + }; + List albazFusionMonster = new List + { + CardId.TitanikladTheAshDragon, CardId.SprindTheIrondashDragon, CardId.AlbionTheBrandedDragon, CardId.LubellionTheSearingDragon, CardId.AlbaLenatusTheAbyssDragon, + CardId.MirrorjadeTheIcebladeDragon, CardId.RindbrummTheStrikingDragon, CardId.AlbionTheSanctifireDragon + }; + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.AluberTheJesterOfDespia, _CardId.AshBlossom, _CardId.MaxxC, _CardId.InfiniteImpermanence }}, + {2, new List { CardId.FallenOfAlbaz, CardId.NadirServant, CardId.FusionDeployment, _CardId.CalledByTheGrave }}, + {1, new List { CardId.TheBystialLubellion, CardId.AlbionTheShroudedDragon, CardId.BystialSaronir, CardId.SpringansKitt, CardId.GuidingQuemTheVirtuous, + CardId.BlazingCartesiaTheVirtuous, CardId.TriBrigadeMercourier, CardId.DespianTragedy, CardId.BrandedInWhite, + CardId.BrandedFusion, CardId.GoldSarcophagus, CardId.FoolishBurial, CardId.BrandedInHighSpirits, CardId.BrandedOpening, + _CardId.CrossoutDesignator, CardId.BrandedInRed, CardId.BrandedLost, CardId.BrightestBlazingBrandedKing, + CardId.BrandedBeast, CardId.BrandedRetribution }} + }; + List dangerousDragonIdList = new List { 27548199, 92892239, 98630720, 9753964, 99585850, 24361622, 27572350, 69120785, 96402918, 74294676, 42752141, 18511599, 35103106, 26268488 }; + List notToDestroySpellTrap = new List { 50005218, 6767771 }; + List targetNegateIdList = new List { + _CardId.EffectVeiler, _CardId.InfiniteImpermanence, CardId.GhostMournerMoonlitChill, _CardId.BreakthroughSkill, 74003290, 67037924, + 9753964, 66192538, 23204029, 73445448, 35103106, 30286474, 45002991, 5795980, 38511382, 53742162, 30430448 + }; + + bool summoned = false; + bool enemyActivateMaxxC = false; + bool enemyActivateLockBird = false; + int dimensionShifterCount = 0; + bool enemyActivateInfiniteImpermanenceFromHand = false; + bool theBystialLubellionSelecting = false; + bool albionTheShroudedDragonSelecting = false; + bool nadirActivated = false; + bool fusionToGYFlag = false; + bool spSummoningAlbaz = false; + int cartesiaSummonGoal = 0; + int sanctifireSelectPositionCount = 0; + int quemSummonFlag = 0; + List cartesiaMaterialList = new List(); + List brandedInRedMaterialList = new List(); + List infiniteImpermanenceList = new List(); + List currentNegateCardList = new List(); + List currentDestroyCardList = new List(); + List sendToGYThisTurn = new List(); + List activatedCardIdList = new List(); + ClientCard fusionTarget = null; + List selectedFusionMaterial = new List(); + List enemyPlaceThisTurn = new List(); + + /// + /// Shuffle List and return a random-order card list + /// + public List ShuffleList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + T tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } + + public bool CheckCanBeTargeted(ClientCard card, bool canBeTarget, CardType selfType) + { + if (card == null) return true; + if (canBeTarget) + { + if (card.IsShouldNotBeTarget()) return false; + if (((int)selfType & (int)CardType.Monster) > 0 && card.IsShouldNotBeMonsterTarget()) return false; + if (((int)selfType & (int)CardType.Spell) > 0 && card.IsShouldNotBeSpellTrapTarget()) return false; + if (((int)selfType & (int)CardType.Trap) > 0 + && (card.IsShouldNotBeSpellTrapTarget() && !card.IsDisabled())) return false; + } + return true; + } + + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + + public int CheckRemainInDeck(params int[] ids) + { + int sum = 0; + foreach (int id in ids) + { + sum += CheckRemainInDeck(id); + } + return sum; + } + + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated(bool disablecheck = true, bool toFieldCheck = false, CardType type = 0) + { + bool isMonster = type == 0 && Card.IsMonster(); + isMonster |= ((int)type & (int)CardType.Monster) != 0; + bool isSpellOrTrap = type == 0 && (Card.IsSpell() || Card.IsTrap()); + isSpellOrTrap |= (((int)type & (int)CardType.Spell) != 0) || (((int)type & (int)CardType.Trap) != 0); + bool isCounter = ((int)type & (int)CardType.Counter) != 0; + if (isSpellOrTrap && toFieldCheck && CheckSpellWillBeNegate(isCounter)) + return true; + if (DefaultCheckWhetherCardIsNegated(Card)) return true; + if (isMonster && (toFieldCheck || Card.Location == CardLocation.MonsterZone)) + { + if ((toFieldCheck && (((int)type & (int)CardType.Link) != 0)) || Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card)) || Bot.MonsterZone.Any(card => CheckNumber41(card))) return true; + } + if (Enemy.HasInSpellZone(CardId.SkillDrain, true, true)) return true; + } + if (disablecheck) return (Card.Location == CardLocation.MonsterZone || Card.Location == CardLocation.SpellZone) && Card.IsDisabled() && Card.IsFaceup(); + return false; + } + + public bool CheckNumber41(ClientCard card) + { + return card != null && card.IsFaceup() && card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled(); + } + + /// + /// Whether spell or trap will be negate. If so, return true. + /// + /// is counter trap + /// check target + /// + public bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + // target default set + if (target == null) target = Card; + // won't negate if not on field + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + // negate judge + if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap() && (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true))) return true; + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) return true; + } + // how to get here? + return false; + } + + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (notToNegateIdList.Contains(lastcard.Id)) return false; + if (DefaultCheckWhetherCardIsNegated(lastcard)) return false; + if (Duel.Turn == 1 && lastcard.IsCode(_CardId.MaxxC)) return false; + + return true; + } + + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && (Duel.Player == 0 || Bot.GetMonsterCount() > 0)) return true; + return false; + } + + public bool CheckShouldNoMoreSpSummon() + { + if (CheckAtAdvantage() && enemyActivateMaxxC && !enemyActivateLockBird && (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2)) + { + return true; + } + return false; + } + + public bool CheckWhetherCanSummon() + { + return Duel.Player == 0 && Duel.Phase < DuelPhase.End && !summoned; + } + + /// + /// Check whether cards will be removed. If so, do not send cards to grave. + /// + public bool CheckWhetherWillbeRemoved() + { + if (dimensionShifterCount > 0) return true; + List checkIdList = new List { CardId.BanisheroftheRadiance, CardId.BanisheroftheLight, CardId.MacroCosmos, CardId.DimensionalFissure, + CardId.KashtiraAriseHeart, 58481572 }; + foreach (int cardid in checkIdList) + { + List fields = new List { Bot, Enemy }; + foreach (ClientField cf in fields) + { + if (cf.HasInMonstersZone(cardid, true, false, true) || cf.HasInSpellZone(cardid, true, true)) + { + return true; + } + } + } + return false; + } + + /// + /// Check whether it should be kept in grave to activate effect. + /// If should, return true. + /// + /// + /// + public bool CheckWhetherShouldKeepInGrave(ClientCard c) + { + if (c.IsCode(CardId.GranguignolTheDuskDragon) && c.Location == CardLocation.Grave) return true; + if (!c.IsCode(new int[] { CardId.AlbaLenatusTheAbyssDragon, CardId.AlbionTheBrandedDragon, CardId.TitanikladTheAshDragon, CardId.DespianLuluwalilith, + CardId.SprindTheIrondashDragon})) + { + return false; + } + return sendToGYThisTurn.Contains(c) && c.Location == CardLocation.Grave; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + ClientCard floodagateCard = Enemy.GetMonsters().Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsFloodgate() && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (floodagateCard != null) return floodagateCard; + + ClientCard dangerCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (dangerCard != null) return dangerCard; + + ClientCard invincibleCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (invincibleCard != null) return invincibleCard; + + ClientCard equippedCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.EquipCards.Count > 0 && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (equippedCard != null) return equippedCard; + + ClientCard enemyExtraMonster = Enemy.MonsterZone.Where(c => c != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && (c.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) || (c.HasType(CardType.Link) && c.LinkCount >= 2)) + && CheckCanBeTargeted(c, canBeTarget, selfType) && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (enemyExtraMonster != null) return enemyExtraMonster; + + ClientCard activatingAlbaz = Enemy.MonsterZone.FirstOrDefault(c => c != null && c.IsCode(CardId.FallenOfAlbaz) && !c.IsDisabled() + && !currentDestroyCardList.Contains(c) && !currentNegateCardList.Contains(c) && Duel.CurrentChain.Contains(c)); + if (activatingAlbaz != null) return activatingAlbaz; + + if (attack >= 0) + { + if (attack == 0) + attack = Util.GetBestAttack(Bot); + ClientCard betterCard = Enemy.MonsterZone.Where(card => card != null + && card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && CheckCanBeTargeted(card, canBeTarget, selfType) + && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (betterCard != null) return betterCard; + } + return null; + } + + public bool CheckShouldNotIgnore(ClientCard cards, bool ignore = false) + { + return !ignore || (!currentDestroyCardList.Contains(cards) && !currentNegateCardList.Contains(cards)); + } + + /// + /// check enemy's dangerous card in grave + /// + public List GetDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantom) || card.HasSetcode(SetcodeHorus))).ToList(); + List dangerMonsterIdList = new List{ + 99937011, 63542003, 9411399, 28954097, 30680659 + }; + result.AddRange(Enemy.Graveyard.GetMatchingCards(card => dangerMonsterIdList.Contains(card.Id))); + return result; + } + + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreSpells = false, CardType selfType = 0) + { + List resultList = new List(); + + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count > 0) resultList.AddRange(floodagateList); + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).ToList(); + if (problemEnemySpellList.Count > 0) resultList.AddRange(ShuffleList(problemEnemySpellList)); + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) resultList.AddRange(dangerList); + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count > 0) resultList.AddRange(invincibleList); + + List enemyMonsters = Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c)).OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count > 0) + { + foreach (ClientCard target in enemyMonsters) + { + if ((target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && !resultList.Contains(target) && CheckCanBeTargeted(target, canBeTarget, selfType) + ) + { + resultList.Add(target); + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !currentDestroyCardList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) && CheckCanBeTargeted(c, canBeTarget, selfType) + && !notToDestroySpellTrap.Contains(c.Id)).ToList(); + if (spells.Count > 0 && !ignoreSpells) resultList.AddRange(ShuffleList(spells)); + + return resultList; + } + + public List GetNormalEnemyTargetList(bool canBeTarget = true, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + List targetList = GetProblematicEnemyCardList(canBeTarget, selfType: selfType); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && enemyPlaceThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && !enemyPlaceThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList())); + + return targetList; + } + + public List GetMonsterListForTargetNegate(bool canBeTarget = false, CardType selfType = 0) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; + } + + // negate before used + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && CheckCanBeTargeted(card, canBeTarget, selfType) + && !currentNegateCardList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + // negate monster effect on the field + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && CheckCanBeTargeted(chainingCard, canBeTarget, selfType) && !currentNegateCardList.Contains(chainingCard)) + { + resultList.Add(chainingCard); + } + } + + return resultList; + } + + public override BattlePhaseAction OnBattle(IList attackers, IList defenders) + { + if (attackers.Count() > 0 && defenders.Count() > 0) + { + List sortedAttacker = attackers.OrderBy(card => card.Attack).ToList(); + ClientCard abyssDragon = attackers.FirstOrDefault(c => c.IsCode(CardId.AlbaLenatusTheAbyssDragon) && !c.IsDisabled()); + if (abyssDragon != null) + { + sortedAttacker.Remove(abyssDragon); + sortedAttacker.Insert(0, abyssDragon); + } + for (int k = 0; k < sortedAttacker.Count; ++k) + { + ClientCard attacker = sortedAttacker[k]; + attacker.IsLastAttacker = k == sortedAttacker.Count - 1; + BattlePhaseAction result = OnSelectAttackTarget(attacker, defenders); + if (result != null) + return result; + } + } + + return base.OnBattle(attackers, defenders); + } + + /// + /// go first + /// + public override bool OnSelectHand() + { + return true; + } + + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (currentSolvingChain != null) + { + if (currentSolvingChain.Controller == 1 && currentSolvingChain.IsCode(_CardId.EvenlyMatched)) + { + Logger.DebugWriteLine("=== Evenly Matched activated."); + List banishList = new List(); + List botMonsters = Bot.GetMonsters().Where(card => !card.HasType(CardType.Token)).ToList(); + + // monster + List faceDownMonsters = botMonsters.Where(card => card.IsFacedown()).ToList(); + banishList.AddRange(faceDownMonsters); + List dumpMainMonsterList = botMonsters.Where(card => !banishList.Contains(card) + && CheckRemainInDeck(card.Id) > 0).ToList(); + dumpMainMonsterList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(dumpMainMonsterList); + + // spells + List faceUpSpells = Bot.GetSpells().Where(c => c.IsFaceup()).ToList(); + banishList.AddRange(ShuffleList(faceUpSpells)); + List faceDownSpells = Bot.GetSpells().Where(c => c.IsFacedown()).ToList(); + banishList.AddRange(ShuffleList(faceDownSpells)); + + List uniqueMainMonster = botMonsters.Where(card => !banishList.Contains(card) + && !card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && CheckRemainInDeck(card.Id) == 0).ToList(); + uniqueMainMonster.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(uniqueMainMonster); + + List dumpExtraMonsterList = botMonsters.Where(card => !banishList.Contains(card) + && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && Bot.HasInExtra(card.Id)).ToList(); + dumpExtraMonsterList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(dumpExtraMonsterList); + + List uniqueExtraMonsterList = botMonsters.Where(card => !banishList.Contains(card) + && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && !Bot.HasInExtra(card.Id)).ToList(); + uniqueExtraMonsterList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(uniqueExtraMonsterList); + + return Util.CheckSelectCount(banishList, cards, min, max); + } + + // search operation + if (hint == HintMsg.AddToHand) + { + Dictionary> checkDict = new Dictionary>(); + + switch (currentSolvingChain.Id) + { + case CardId.AluberTheJesterOfDespia: + case CardId.AluberTheJesterOfDespia + 1: + case CardId.SpringansKitt: + checkDict = new Dictionary> { + {CardId.BrandedFusion, () => BrandedFusionActivateCheck()}, + {CardId.BrandedLost, () => { + if (Duel.Player == 0 && Duel.Phase >= DuelPhase.End) return false; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedFusion) && BrandedFusionActivateCheck()) return true; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedInWhite) && BrandedInWhiteActivateCheck()) return true; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedInRed) && BrandedInRedActivateCheck() != null) return true; + if (!summoned && Bot.HasInHand(CardId.FallenOfAlbaz) && CheckAlbazFusion()) return true; + if ((Bot.HasInMonstersZone(CardId.BlazingCartesiaTheVirtuous) || (!summoned && Bot.HasInHand(CardId.BlazingCartesiaTheVirtuous)))) return true; + return false; + } + }, + {CardId.BrandedInHighSpirits, BrandedInHighSpiritsActivateCheck}, + {CardId.BrandedInRed, () => (Duel.Phase == DuelPhase.End && nadirActivated) || BrandedInRedActivateCheck() != null }, + {CardId.BrandedInWhite, BrandedInWhiteActivateCheck }, + {CardId.BrandedRetribution, () => cards.Any(c => c.IsCode(CardId.BrandedRetribution) && c.Location == CardLocation.Removed) }, + {CardId.BrightestBlazingBrandedKing, () => Bot.GetMonsters().Any(c => c.IsFaceup() && c.IsCode(albazFusionMonster)) }, + {CardId.BrandedOpening, () => Bot.Hand.Count > 2 } + }; + break; + case CardId.NadirServant: + if (!summoned) + { + ClientCard quem = cards.FirstOrDefault(c => c.IsCode(CardId.GuidingQuemTheVirtuous)); + if (quem != null) + { + return Util.CheckSelectCount(new List { quem }, cards, min, max); + } + } + List locList = new List { CardLocation.Grave, CardLocation.Deck }; + if (Bot.HasInGraveyard(CardId.RindbrummTheStrikingDragon) && cards.Where(c => c.IsOriginalCode(CardId.FallenOfAlbaz) && c.Location == CardLocation.Grave).Count() == 1) + { + locList = new List { CardLocation.Deck, CardLocation.Grave }; + } + foreach (int checkId in new[] { CardId.FallenOfAlbaz, CardId.GuidingQuemTheVirtuous }) + { + foreach (CardLocation loc in locList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsOriginalCode(checkId) && c.Location == loc); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + case CardId.BrandedLost: + case CardId.TriBrigadeMercourier: + case CardId.BrandedInHighSpirits: + checkDict = new Dictionary>{ + {CardId.TriBrigadeMercourier, () => Bot.GetMonsters().Any(c => c.IsFaceup() && c.IsCode(albazFusionMonster)) + || (Bot.HasInMonstersZone(CardId.BlazingCartesiaTheVirtuous) && Bot.HasInHandOrHasInMonstersZone(CardId.FallenOfAlbaz))}, + {CardId.SpringansKitt, () => CheckWhetherCanSummon() && !activatedCardIdList.Contains(CardId.SpringansKitt + 1) }, + {CardId.FallenOfAlbaz, () => (CheckWhetherCanSummon() && CheckAlbazFusion()) || Bot.HasInMonstersZone(CardId.BlazingCartesiaTheVirtuous) }, + {CardId.GuidingQuemTheVirtuous, () => CheckWhetherCanSummon() }, + {CardId.BlazingCartesiaTheVirtuous, () => CheckWhetherCanSummon() || (!CheckShouldNoMoreSpSummon() && Bot.HasInMonstersZoneOrInGraveyard(CardId.FallenOfAlbaz))}, + {CardId.AlbionTheShroudedDragon, () => !CheckWhetherWillbeRemoved() && !activatedCardIdList.Contains(CardId.AlbionTheShroudedDragon) }, + }; + break; + case CardId.AlbaLenatusTheAbyssDragon: + checkDict = new Dictionary>{ + {CardId.BrandedFusion, () => BrandedFusionActivateCheck(false)}, + {CardId.FusionDeployment, () => true} + }; + break; + default: + break; + } + + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + + switch (currentSolvingChain.Id) + { + // for lubellion + case CardId.TheBystialLubellion: + { + Dictionary> lubellionCheckDict = new Dictionary>(); + if (hint == HintMsg.ToField) + { + lubellionCheckDict.Add(CardId.BrandedLost, () => { + bool fusionFlag = Bot.HasInHandOrHasInMonstersZone(CardId.BlazingCartesiaTheVirtuous); + if (!activatedCardIdList.Contains(CardId.BrandedFusion)) + { + if (Bot.HasInHand(CardId.BrandedFusion) || (!summoned && CheckRemainInDeck(CardId.BrandedFusion) > 0 && Bot.HasInHand(new int[] { + CardId.AluberTheJesterOfDespia, CardId.SpringansKitt + }))) + { + fusionFlag = true; + } + } + fusionFlag |= !summoned && Bot.HasInHand(CardId.FallenOfAlbaz) && CheckAlbazFusion(); + fusionFlag |= Bot.HasInHandOrInSpellZone(CardId.BrandedInWhite) && BrandedInWhiteActivateCheck(); + fusionFlag |= Bot.HasInHandOrInSpellZone(CardId.BrandedInRed) && BrandedInRedActivateCheck() != null; + + return fusionFlag; + }); + lubellionCheckDict.Add(CardId.BrandedBeast, () => true); + } + else if (hint == HintMsg.AddToHand) + { + lubellionCheckDict.Add(CardId.BystialSaronir, () => true); + } + + foreach (KeyValuePair> pair in lubellionCheckDict) + { + ClientCard target = cards.FirstOrDefault(c => c.Id == pair.Key); + if (target != null && pair.Value()) + { + SelectSTPlace(target, false); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for albaz + case CardId.FallenOfAlbaz: + case CardId.FallenOfAlbaz + 1: + if (hint == HintMsg.SpSummon) + { + List fusionTargetIdList = new List { + CardId.MirrorjadeTheIcebladeDragon, CardId.AlbaLenatusTheAbyssDragon, CardId.AlbionTheBrandedDragon, CardId.AlbionTheSanctifireDragon, + CardId.LubellionTheSearingDragon, CardId.BorreloadFuriousDragon, CardId.TitanikladTheAshDragon, CardId.RindbrummTheStrikingDragon + }; + foreach (int targetId in fusionTargetIdList) + { + if (targetId == CardId.LubellionTheSearingDragon && Bot.Hand.Count == 0) continue; + ClientCard target = cards.FirstOrDefault(c => c.IsCode(targetId)); + if (target != null) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + if (hint == HintMsg.FusionMaterial) + { + if (cards.Count == 1) + { + selectedFusionMaterial.AddRange(cards); + return Util.CheckSelectCount(cards, cards, min, max); + } + // select best enemy monster + List sortedResult = cards.OrderByDescending(card => card.GetDefensePower()).ToList(); + selectedFusionMaterial.Add(sortedResult[0]); + return Util.CheckSelectCount(sortedResult, cards, min, max); + } + break; + + // for quem + case CardId.GuidingQuemTheVirtuous: + { + Dictionary> quemCheckDict = new Dictionary> + { + {CardId.BlazingCartesiaTheVirtuous, () => sendToGYThisTurn.Any(c => c.IsCode(CardId.AlbionTheBrandedDragon)) && CheckRemainInDeck(CardId.BrandedInHighSpirits) > 0 }, + {CardId.BrandedFusion, () => Bot.HasInGraveyard(CardId.BrandedRetribution) }, + {CardId.FallenOfAlbaz, () => !Bot.HasInGraveyard(CardId.FallenOfAlbaz) }, + {CardId.TriBrigadeMercourier, () => Bot.HasInHandOrInSpellZone(CardId.BrandedInWhite) }, + {CardId.BrandedRetribution, () => true }, + {CardId.BrightestBlazingBrandedKing, () => !Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.IsCode(albazFusionMonster) && fusionToGYFlag) }, + {CardId.BrandedInHighSpirits, () => fusionToGYFlag }, + {CardId.AlbionTheShroudedDragon, () => true }, + }; + + foreach (KeyValuePair> pair in quemCheckDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for cartesia + case CardId.BlazingCartesiaTheVirtuous: + case CardId.BlazingCartesiaTheVirtuous + 1: + if (hint == HintMsg.SpSummon) + { + // fix material list + cartesiaMaterialList = cartesiaMaterialList.Where(c => c != null && (c.Location == CardLocation.MonsterZone || c.Location == CardLocation.Hand)).ToList(); + + // filter normal material + List materialList = Bot.MonsterZone.Where(c => c != null && c.GetDefensePower() <= 2500 && !c.IsCode(cannotBeFusionMaterialIdList)).ToList(); + materialList.AddRange(Bot.Hand.Where(c => c.IsMonster() + && !(CheckWhetherCanSummon() && + ((!activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && c.IsCode(CardId.AluberTheJesterOfDespia)) + || (!activatedCardIdList.Contains(CardId.SpringansKitt) && c.IsCode(CardId.SpringansKitt))) + ) + ) + ); + + if (cartesiaSummonGoal > 0) + { + BlazingCartesiaTheVirtuousFusionCheck(cards, cartesiaSummonGoal, materialList, cartesiaMaterialList, out ClientCard _fusionTarget1, out _); + if (_fusionTarget1 != null) + { + fusionTarget = _fusionTarget1; + return Util.CheckSelectCount(new List { fusionTarget }, cards, min, max); + } + } + BlazingCartesiaTheVirtuousFusionCheck(cards, 0, materialList, cartesiaMaterialList, out ClientCard _fusionTarget2, out _); + if (_fusionTarget2 != null) + { + fusionTarget = _fusionTarget2; + return Util.CheckSelectCount(new List { fusionTarget }, cards, min, max); + } + + } + if (hint == HintMsg.FusionMaterial) + { + List mustSelectMaterialList = cartesiaMaterialList.Intersect(cards).ToList(); + if (mustSelectMaterialList != null && mustSelectMaterialList.Count > 0) + { + selectedFusionMaterial.Add(mustSelectMaterialList[0]); + return Util.CheckSelectCount(mustSelectMaterialList, cards, min, max); + } + + ClientCard lubellion = cards.FirstOrDefault(c => c != null && c.IsCode(CardId.TheBystialLubellion) && c.Location == CardLocation.MonsterZone); + if (lubellion != null && !Bot.HasInHandOrInSpellZone(CardId.BrandedBeast)) + { + if (activatedCardIdList.Contains(CardId.TheBystialLubellion + 1) || CheckRemainInDeck(CardId.BrandedLost, CardId.BrandedBeast) == 0) + { + if (Util.IsTurn1OrMain2() || Enemy.MonsterZone.Count(c => c != null && c.GetDefensePower() < 2500) > 0) + { + return Util.CheckSelectCount(new List { lubellion }, cards, min, max); + } + } + } + + ClientCard selectTarget = cards + .Where(c => c.Attack <= 2500 && !(CheckWhetherCanSummon() && + ((!activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && c.IsCode(CardId.AluberTheJesterOfDespia)) + || (!activatedCardIdList.Contains(CardId.SpringansKitt) && c.IsCode(CardId.SpringansKitt))) + ) + ) + .OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (selectTarget != null) + { + selectedFusionMaterial.Add(selectTarget); + return Util.CheckSelectCount(new List { selectTarget }, cards, min, max); + } + selectTarget = cards.OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (selectTarget != null) + { + selectedFusionMaterial.Add(selectTarget); + return Util.CheckSelectCount(new List { selectTarget }, cards, min, max); + } + } + break; + + // for nadir + case CardId.NadirServant: + if (hint == HintMsg.ToGrave) + { + if (summoned) + { + if (CheckRemainInDeck(CardId.BlazingCartesiaTheVirtuous) > 0) + { + ClientCard lulu = cards.FirstOrDefault(c => c.IsOriginalCode(CardId.DespianLuluwalilith)); + if (lulu != null) + { + return Util.CheckSelectCount(new List { lulu }, cards, min, max); + } + } + if (!Bot.MonsterZone.Any(c => c != null && c.HasType(CardType.Fusion)) && CheckRemainInDeck(CardId.SpringansKitt) > 0) + { + ClientCard ironDragon = cards.FirstOrDefault(c => c.IsOriginalCode(CardId.SprindTheIrondashDragon)); + if (ironDragon != null) + { + return Util.CheckSelectCount(new List { ironDragon }, cards, min, max); + } + } + } + NadirServantActivateCheck(cards, true, out ClientCard target); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + + // for deployment + case CardId.FusionDeployment: + { + int summonId = FusionDeploymentSpSummonTarget(); + if (summonId > 0) + { + if (hint == HintMsg.Confirm) + { + if (summonId == CardId.BlazingCartesiaTheVirtuous) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(CardId.GranguignolTheDuskDragon)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + else if (summonId == CardId.FallenOfAlbaz) + { + List shuffleList = ShuffleList(new List(cards)); + foreach (ClientCard target in shuffleList) + { + if (target.IsCode(albazFusionMonster)) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + } + if (hint == HintMsg.SpSummon) + { + foreach (ClientCard target in cards) + { + if (target.IsCode(summonId)) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + } + } + break; + + // for branded in white + case CardId.BrandedInWhite: + if (hint == HintMsg.SpSummon) + { + BrandedInWhiteFusionTarget(cards, out ClientCard target); + if (target != null) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + if (hint == HintMsg.FusionMaterial && fusionTarget != null) + { + if (fusionTarget.IsCode(CardId.BorreloadFuriousDragon)) + { + // select 2 dark dragon monster + foreach (CardLocation loc in new[] { CardLocation.Grave, CardLocation.Hand, CardLocation.MonsterZone }) + { + List cardsInLoc = cards.Where(c => c.Location == loc).OrderBy(c => c.GetDefensePower()).ToList(); + int banishedAlbazCount = Bot.Banished.Where(c => c.IsOriginalCode(CardId.FallenOfAlbaz)).Count(); + banishedAlbazCount += selectedFusionMaterial.Where(c => c.IsOriginalCode(CardId.FallenOfAlbaz)).Count(); + foreach (ClientCard target in cardsInLoc) + { + // keep albaz + if (target.IsOriginalCode(CardId.FallenOfAlbaz) && banishedAlbazCount > 0) + { + continue; + } + selectedFusionMaterial.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + if (fusionTarget.IsCode(CardId.DespianQuaeritis)) + { + // select despain + if (selectedFusionMaterial.Count == 0) + { + foreach (CardLocation loc in new[] { CardLocation.Grave, CardLocation.Hand, CardLocation.MonsterZone }) + { + List cardsInLoc = cards.Where(c => c.Location == loc && c.HasSetcode(SetcodeDespain) && (loc != CardLocation.Grave || !CheckWhetherShouldKeepInGrave(c))) + .OrderBy(c => c.GetDefensePower()).ToList(); + if (cardsInLoc.Count > 0) + { + selectedFusionMaterial.Add(cardsInLoc[0]); + return Util.CheckSelectCount(cardsInLoc, cards, min, max); + } + } + } + // select light/dark + else + { + foreach (CardLocation loc in new[] { CardLocation.Grave, CardLocation.Hand, CardLocation.MonsterZone }) + { + List cardsInLoc = cards.Where(c => c.Location == loc && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark) && (loc != CardLocation.Grave || !CheckWhetherShouldKeepInGrave(c))) + .OrderBy(c => c.GetDefensePower()).ToList(); + if (cardsInLoc.Count > 0) + { + if (!activatedCardIdList.Contains(CardId.TriBrigadeMercourier + 1)) + { + ClientCard mercourier = cardsInLoc.FirstOrDefault(c => c.IsCode(CardId.TriBrigadeMercourier)); + if (mercourier != null) + { + selectedFusionMaterial.Add(mercourier); + return Util.CheckSelectCount(new List { mercourier }, cards, min, max); + } + } + if (!activatedCardIdList.Contains(CardId.DespianTragedy) && CheckRemainInDeck(CardId.AluberTheJesterOfDespia, CardId.GuidingQuemTheVirtuous) > 0) + { + ClientCard tragedy = cardsInLoc.FirstOrDefault(c => c.IsCode(CardId.DespianTragedy)); + if (tragedy != null) + { + selectedFusionMaterial.Add(tragedy); + return Util.CheckSelectCount(new List { tragedy }, cards, min, max); + } + } + + selectedFusionMaterial.Add(cardsInLoc[0]); + return Util.CheckSelectCount(cardsInLoc, cards, min, max); + } + } + } + } + if (fusionTarget.IsCode(CardId.GuardianChimera)) + { + List goalMaterialList = ChimeraFusionMaterialList().Intersect(cards).ToList(); + if (goalMaterialList.Count > 0) + { + return Util.CheckSelectCount(goalMaterialList, cards, min, max); + } + } + if (fusionTarget.IsCode(albazFusionMonster)) + { + // selecting albaz + if (selectedFusionMaterial.Count == 0) + { + foreach (CardLocation loc in new[] { CardLocation.Grave, CardLocation.MonsterZone, CardLocation.Hand }) + { + ClientCard albaz = cards.Where(c => c.IsCode(CardId.FallenOfAlbaz)).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (albaz != null) + { + selectedFusionMaterial.Add(albaz); + return Util.CheckSelectCount(new List { albaz }, cards, min, max); + } + } + } + else + { + if (fusionTarget.IsOriginalCode(CardId.AlbaLenatusTheAbyssDragon) && cancelable) + { + return null; + } + if (Util.IsTurn1OrMain2() && !CheckWhetherWillbeRemoved()) + { + ClientCard duskDragon = cards.FirstOrDefault(c => c.IsCode(CardId.GranguignolTheDuskDragon) && c.Location == CardLocation.MonsterZone); + if (duskDragon != null) + { + selectedFusionMaterial.Add(duskDragon); + return Util.CheckSelectCount(new List { duskDragon }, cards, min, max); + } + } + List> funcList = new List> + { + (c) => c.Location == CardLocation.Grave && !CheckWhetherShouldKeepInGrave(c), + (c) => c.Location == CardLocation.MonsterZone && c.GetDefensePower() <= 2000, + (c) => c.Location == CardLocation.Hand, + (c) => c.Location == CardLocation.Grave, + (c) => c.Location == CardLocation.MonsterZone + }; + foreach (Func func in funcList) + { + List targetList = cards.Where(c => func(c)).OrderBy(c => c.GetDefensePower()).ToList(); + if (targetList.Count > 0) + { + selectedFusionMaterial.Add(targetList[0]); + return Util.CheckSelectCount(new List { targetList[0] }, cards, min, max); + } + } + } + } + } + break; + + // for branded fusion + case CardId.BrandedFusion: + if (hint == HintMsg.SpSummon) + { + Dictionary> brandedFusionCheckDict = new Dictionary> + { + {CardId.TitanikladTheAshDragon, () => Enemy.HasInMonstersZone(CardId.KashtiraAriseHeart) }, + {CardId.RindbrummTheStrikingDragon, () => CheckWhetherWillbeRemoved() && CheckRemainInDeck(CardId.TriBrigadeMercourier) > 0}, + {CardId.AlbionTheSanctifireDragon, () => CheckShouldNoMoreSpSummon()}, + {CardId.AlbionTheBrandedDragon, () => { + bool checkFlag = Bot.Graveyard.Any(c => c != null && c.IsMonster() && c.HasAttribute(CardAttribute.Dark) && !c.IsCode(cannotBeFusionMaterialIdList)); + checkFlag |= Bot.HasInHandOrHasInMonstersZone(CardId.TriBrigadeMercourier); + checkFlag |= Bot.GetMonsters().Any(c => c.GetDefensePower() <= 1800 && c.HasAttribute(CardAttribute.Dark) && !c.IsCode(cannotBeFusionMaterialIdList)); + return checkFlag; + } }, + {CardId.LubellionTheSearingDragon, () => !CheckWhetherNegated(true, true, CardType.Monster) && Bot.Hand.Count > 0 }, + {CardId.MirrorjadeTheIcebladeDragon, () => Bot.HasInMonstersZone(new List{ + CardId.GranguignolTheDuskDragon, CardId.AlbionTheBrandedDragon, CardId.LubellionTheSearingDragon}) } + }; + + foreach (KeyValuePair> pair in brandedFusionCheckDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + if (hint == HintMsg.FusionMaterial) + { + // selecting albaz + if (selectedFusionMaterial.Count == 0) + { + foreach (CardLocation loc in new[] { CardLocation.Deck, CardLocation.Hand, CardLocation.MonsterZone }) + { + ClientCard target = cards.FirstOrDefault(c => c.IsOriginalCode(CardId.FallenOfAlbaz) && c.Location == loc); + if (target != null) + { + selectedFusionMaterial.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + // selecting another material + if (fusionTarget != null) + { + Dictionary> materialDict = new Dictionary> + { + { CardId.AlbionTheSanctifireDragon, new List{CardId.BlazingCartesiaTheVirtuous, _CardId.EffectVeiler, CardId.GuidingQuemTheVirtuous} }, + { CardId.MirrorjadeTheIcebladeDragon, new List{CardId.GranguignolTheDuskDragon, CardId.AlbionTheBrandedDragon, CardId.DespianLuluwalilith } }, + { CardId.LubellionTheSearingDragon, new List{ + CardId.DespianTragedy, CardId.BystialSaronir, CardId.AlbionTheShroudedDragon, CardId.AluberTheJesterOfDespia, CardId.TriBrigadeMercourier } }, + { CardId.TitanikladTheAshDragon, new List{CardId.TheBystialLubellion, CardId.AlbionTheShroudedDragon, CardId.BystialSaronir} }, + { CardId.RindbrummTheStrikingDragon, new List{CardId.TriBrigadeMercourier, CardId.SpringansKitt } }, + { CardId.AlbionTheBrandedDragon, new List{CardId.TheBystialLubellion, CardId.BlazingCartesiaTheVirtuous, CardId.GuidingQuemTheVirtuous } } + }; + materialDict.TryGetValue(fusionTarget.GetOriginCode(), out List checkIdList); + if (checkIdList != null && checkIdList.Count > 0) + { + foreach (CardLocation location in new List { CardLocation.Deck, CardLocation.Hand, CardLocation.MonsterZone }) + { + foreach (int checkId in checkIdList) + { + ClientCard target = cards.FirstOrDefault(c => c.Location == location && c.IsCode(checkId)); + if (target != null) + { + selectedFusionMaterial.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + } + } + } + break; + + // for Sarcophagus + case CardId.GoldSarcophagus: + { + GoldSarcophagusTarget(cards, out ClientCard sarcophagusTarget); + if (sarcophagusTarget != null) + { + return Util.CheckSelectCount(new List { sarcophagusTarget }, cards, min, max); + } + } + break; + + // for burial + case CardId.FoolishBurial: + case CardId.FoolishBurial + 1: + { + FoolishBurialTarget(cards, out ClientCard burialTarget); + if (burialTarget != null) + { + return Util.CheckSelectCount(new List { burialTarget }, cards, min, max); + } + } + break; + + // for high spirit + case CardId.BrandedInHighSpirits: + // confirm + if (hint == HintMsg.Confirm) + { + if (Duel.Phase == DuelPhase.End && Bot.HasInMonstersZone(CardId.GuidingQuemTheVirtuous)) + { + ClientCard cartesia = cards.FirstOrDefault(c => c.IsOriginalCode(CardId.BlazingCartesiaTheVirtuous)); + if (cartesia != null) + { + fusionTarget = cartesia; + return Util.CheckSelectCount(new List { cartesia }, cards, min, max); + } + } + bool activatingShroudedFlag = Duel.CurrentChain.Any(c => c.IsOriginalCode(CardId.AlbionTheShroudedDragon) && c.Location == CardLocation.Hand); + if (activatingShroudedFlag) + { + ClientCard shrouded = cards.FirstOrDefault(c => c.IsOriginalCode(CardId.AlbionTheShroudedDragon)); + if (shrouded != null) + { + fusionTarget = shrouded; + return Util.CheckSelectCount(new List { shrouded }, cards, min, max); + } + } + List discardIdList = new List { + CardId.BystialSaronir, CardId.AlbionTheShroudedDragon, CardId.TheBystialLubellion, CardId.BlazingCartesiaTheVirtuous, + CardId.FallenOfAlbaz, CardId.TriBrigadeMercourier + }; + foreach (int discardId in discardIdList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsOriginalCode(discardId)); + if (target != null) + { + // lubellion check + if (discardId == CardId.TheBystialLubellion && Duel.Player == 0 && (Duel.Phase <= DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) + && CheckRemainInDeck(CardId.BystialSaronir) > 0 && !activatedCardIdList.Contains(CardId.TheBystialLubellion)) + { + continue; + } + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + // discard + if (hint == HintMsg.ToGrave) + { + List discardIdList = new List { + CardId.AlbionTheBrandedDragon, CardId.TitanikladTheAshDragon, CardId.RindbrummTheStrikingDragon, CardId.AlbaLenatusTheAbyssDragon, + CardId.GranguignolTheDuskDragon + }; + foreach (int discardId in discardIdList) + { + if (sendToGYThisTurn.Any(c => c.IsOriginalCode(discardId))) continue; + ClientCard target = cards.FirstOrDefault(c => c.IsOriginalCode(discardId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for opening + case CardId.BrandedOpening: + if (hint == HintMsg.OperateCard) + { + Dictionary> openingCheckDict = new Dictionary>{ + {CardId.AluberTheJesterOfDespia, () => !activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && !DefaultCheckWhetherCardIdIsNegated(CardId.AluberTheJesterOfDespia) + && !(CheckWhetherCanSummon() && Bot.HasInHand(CardId.AluberTheJesterOfDespia))}, + {CardId.GuidingQuemTheVirtuous, () => true }, + {CardId.DespianTragedy, () => true } + }; + foreach (KeyValuePair> pair in openingCheckDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for branded in red + case CardId.BrandedInRed: + if (hint == HintMsg.SpSummon) + { + // filter normal material + List materialList = Bot.MonsterZone.Where(c => c != null && c.GetDefensePower() <= 2500 && !c.IsCode(cannotBeFusionMaterialIdList)).ToList(); + materialList.AddRange(Bot.Hand.Where(c => c.IsMonster() + && !(CheckWhetherCanSummon() && + ((!activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && c.IsCode(CardId.AluberTheJesterOfDespia)) + || (!activatedCardIdList.Contains(CardId.SpringansKitt) && c.IsCode(CardId.SpringansKitt))) + ) + ) + ); + + BrandedInRedFusionCheck(cards, 0, materialList, brandedInRedMaterialList, out ClientCard _fusionTarget, out _); + if (_fusionTarget != null) + { + fusionTarget = _fusionTarget; + return Util.CheckSelectCount(new List { fusionTarget }, cards, min, max); + } + + } + if (hint == HintMsg.FusionMaterial) + { + List mustSelectMaterialList = brandedInRedMaterialList.Intersect(cards).ToList(); + if (mustSelectMaterialList != null && mustSelectMaterialList.Count > 0) + { + selectedFusionMaterial.Add(mustSelectMaterialList[0]); + return Util.CheckSelectCount(mustSelectMaterialList, cards, min, max); + } + + ClientCard selectTarget = cards + .Where(c => c.Attack <= 2500 && !(CheckWhetherCanSummon() && + ((!activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && c.IsCode(CardId.AluberTheJesterOfDespia)) + || (!activatedCardIdList.Contains(CardId.SpringansKitt) && c.IsCode(CardId.SpringansKitt))) + ) + ) + .OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (selectTarget != null) + { + selectedFusionMaterial.Add(selectTarget); + return Util.CheckSelectCount(new List { selectTarget }, cards, min, max); + } + selectTarget = cards.OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (selectTarget != null) + { + selectedFusionMaterial.Add(selectTarget); + return Util.CheckSelectCount(new List { selectTarget }, cards, min, max); + } + } + break; + + // for branded king + case CardId.BrightestBlazingBrandedKing: + if (hint == HintMsg.Faceup) + { + List targetIdList = new List + { + CardId.MirrorjadeTheIcebladeDragon, CardId.AlbionTheSanctifireDragon, CardId.RindbrummTheStrikingDragon + }; + List> funcList = new List> + { + (c) => Duel.CurrentChain.Contains(c), + (c) => true + }; + foreach (Func func in funcList) + { + List chainedList = cards.Where(c => func(c)).ToList(); + if (chainedList.Count > 0) + { + foreach (int checkId in targetIdList) + { + ClientCard target = chainedList.FirstOrDefault(c => c.IsOriginalCode(checkId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + ClientCard otherChainTarget = chainedList.FirstOrDefault(c => Duel.CurrentChain.Contains(c)); + if (otherChainTarget != null) + { + return Util.CheckSelectCount(new List { otherChainTarget }, cards, min, max); + } + } + } + } + break; + + // for retribution + case CardId.BrandedRetribution: + { + ClientCard searing = cards.FirstOrDefault(c => c.IsCode(CardId.LubellionTheSearingDragon)); + if (searing != null) + { + selectedFusionMaterial.Add(searing); + return Util.CheckSelectCount(new List { searing }, cards, min, max); + } + List checkIdList = new List { CardId.AlbionTheBrandedDragon, CardId.MirrorjadeTheIcebladeDragon, CardId.TitanikladTheAshDragon, CardId.AlbaLenatusTheAbyssDragon, + CardId.AlbionTheSanctifireDragon, CardId.SprindTheIrondashDragon}; + foreach (int checkId in checkIdList) + { + List gravePriorityList = cards.Where(c => c != null && c.IsCode(checkId) && c.Location == CardLocation.Grave && !CheckWhetherShouldKeepInGrave(c)).ToList(); + if (gravePriorityList.Count > 0) + { + selectedFusionMaterial.Add(gravePriorityList[0]); + return Util.CheckSelectCount(new List { gravePriorityList[0] }, cards, min, max); + } + } + List graveList = cards.Where(c => c != null && c.Location == CardLocation.Grave).ToList(); + if (graveList.Count > 0) + { + selectedFusionMaterial.Add(graveList[0]); + return Util.CheckSelectCount(new List { graveList[0] }, cards, min, max); + } + ClientCard monsterOnField = cards.Where(c => c != null && c.Location == CardLocation.MonsterZone).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (monsterOnField != null) + { + selectedFusionMaterial.Add(monsterOnField); + return Util.CheckSelectCount(new List { monsterOnField }, cards, min, max); + } + } + break; + + // for chimera + case CardId.GuardianChimera: + { + List targetList = new List(); + + targetList.AddRange(GetProblematicEnemyCardList(false, false, CardType.Monster)); + int bestBotPower = Util.GetBestPower(Bot); + targetList.AddRange(Enemy.MonsterZone.Where(c => c != null && !targetList.Contains(c) && c.GetDefensePower() >= bestBotPower).OrderByDescending(c => c.GetDefensePower())); + targetList.AddRange(ShuffleList(enemyPlaceThisTurn)); + + return Util.CheckSelectCount(targetList, cards, min, max); + } + break; + + // for sanctifire + case CardId.AlbionTheSanctifireDragon: + if (hint == Util.GetStringId(CardId.AlbionTheSanctifireDragon, 1)) + { + ClientCard albaz = cards.FirstOrDefault(c => c.IsOriginalCode(CardId.FallenOfAlbaz)); + if (albaz != null && CheckAlbazFusion()) + { + return Util.CheckSelectCount(new List { albaz }, cards, min, max); + } + + ClientCard floogate = cards.Where(c => c.IsFloodgate()).OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (floogate != null) + { + return Util.CheckSelectCount(new List { floogate }, cards, min, max); + } + + return Util.CheckSelectCount(cards.OrderByDescending(c => c.GetDefensePower()).ToList(), cards, min, max); + } + break; + + // for mirrorjade + case CardId.MirrorjadeTheIcebladeDragon: + { + List floodgateList = ShuffleList(cards.Where(c => c.Controller == 1 && c.IsFloodgate()).ToList()); + if (floodgateList.Count > 0) + { + return Util.CheckSelectCount(floodgateList, cards, min, max); + } + + List extraMonsterList = cards.Where(c => c.Controller == 1 && ( + c.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (c.HasType(CardType.Link) && c.LinkCount >= 2))).OrderByDescending(c => c.GetDefensePower()).ToList(); + if (extraMonsterList.Count > 0) + { + return Util.CheckSelectCount(extraMonsterList, cards, min, max); + } + + ClientCard worstBotMonster = Util.GetWorstBotMonster(); + int worstBotPower = worstBotMonster == null ? 0 : worstBotMonster.GetDefensePower(); + List betterMonsterList = cards.Where(c => c.Controller == 1 && c.GetDefensePower() >= worstBotPower).OrderByDescending(c => c.GetDefensePower()).ToList(); + if (betterMonsterList.Count > 0) + { + return Util.CheckSelectCount(betterMonsterList, cards, min, max); + } + + List dangerMonsterList = cards.Where(c => c.Controller == 1 && (c.IsMonsterDangerous() || c.IsMonsterInvincible())) + .OrderByDescending(c => c.GetDefensePower()).ToList(); + if (dangerMonsterList.Count > 0) + { + return Util.CheckSelectCount(dangerMonsterList, cards, min, max); + } + + List allEnemyMonsterList = cards.Where(c => c.Controller == 1).OrderByDescending(c => c.IsFacedown() ? 0 : c.GetDefensePower()).ToList(); + if (allEnemyMonsterList.Count > 0) + { + return Util.CheckSelectCount(allEnemyMonsterList, cards, min, max); + } + + ClientCard botMonsterWithEffect = cards.FirstOrDefault(c => c.Controller == 0 && c.IsCode(new[] { CardId.DespianTragedy, CardId.TriBrigadeMercourier })); + if (botMonsterWithEffect != null) + { + return Util.CheckSelectCount(new List { botMonsterWithEffect }, cards, min, max); + } + ClientCard botLubellion = cards.FirstOrDefault(c => c.Controller == 0 && c.IsCode(CardId.TheBystialLubellion)); + if (botLubellion != null) + { + return Util.CheckSelectCount(new List { botLubellion }, cards, min, max); + } + + + List allBotMonster = cards.Where(c => c.Controller == 0).OrderBy(c => c.IsFacedown() ? 0 : c.GetDefensePower()).ToList(); + if (allBotMonster.Count > 0) + { + return Util.CheckSelectCount(allBotMonster, cards, min, max); + } + } + break; + + // for searing dragon + case CardId.LubellionTheSearingDragon: + if (hint == HintMsg.SpSummon) + { + LubellionTheSearingDragonFusionTarget(cards, out ClientCard target); + if (target != null) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + if (hint == HintMsg.FusionMaterial && fusionTarget != null) + { + if (fusionTarget.IsCode(CardId.BorreloadFuriousDragon)) + { + // select 2 dark dragon monster + List> furiousFuncList = new List> + { + (c) => c.IsFaceup() && c.Location == CardLocation.Removed && c.IsCode(CardId.AlbionTheBrandedDragon), + (c) => c.IsFaceup() && c.Location == CardLocation.Removed && c.IsCode(CardId.FallenOfAlbaz), + (c) => c.Location == CardLocation.Grave && c.IsCode(CardId.AlbionTheBrandedDragon) && !CheckWhetherShouldKeepInGrave(c), + (c) => c.IsFaceup() && c.Location == CardLocation.Removed && c.IsCode(CardId.TitanikladTheAshDragon), + (c) => c.IsFaceup() && c.Location == CardLocation.Removed, + (c) => c.Location == CardLocation.Grave && !CheckWhetherShouldKeepInGrave(c), + (c) => c.Location == CardLocation.Grave, + (c) => c.Location == CardLocation.Hand || c.Location == CardLocation.MonsterZone + }; + + foreach (Func func in furiousFuncList) + { + List cardWithFunc = cards.Where(c => func(c)).OrderBy(c => c.GetDefensePower()).ToList(); + if (cardWithFunc.Count > 0) + { + selectedFusionMaterial.Add(cardWithFunc[0]); + return Util.CheckSelectCount(new List { cardWithFunc[0] }, cards, min, max); + } + } + } + List> funcList = new List> + { + (c) => c.IsFaceup() && c.Location == CardLocation.Removed, + (c) => c.Location == CardLocation.Grave && !CheckWhetherShouldKeepInGrave(c), + (c) => c.IsCode(CardId.LubellionTheSearingDragon), + (c) => c.Location == CardLocation.Grave, + (c) => c.Location == CardLocation.MonsterZone, + (c) => c.Location == CardLocation.Hand, + }; + if (selectedFusionMaterial.Count == 0) + { + if (fusionTarget.IsOriginalCode(CardId.DespianQuaeritis)) + { + foreach (Func func in funcList) + { + List cardsWithFunc = cards.Where(c => func(c) && c.HasSetcode(SetcodeDespain)).OrderBy(c => c.GetDefensePower()).ToList(); + if (cardsWithFunc.Count > 0) + { + selectedFusionMaterial.Add(cardsWithFunc[0]); + return Util.CheckSelectCount(cardsWithFunc, cards, min, max); + } + } + } + if (fusionTarget.IsCode(albazFusionMonster)) + { + foreach (Func func in funcList) + { + List cardsWithFunc = cards.Where(c => func(c) && c.IsCode(CardId.FallenOfAlbaz)).OrderBy(c => c.GetDefensePower()).ToList(); + if (cardsWithFunc.Count > 0) + { + selectedFusionMaterial.Add(cardsWithFunc[0]); + return Util.CheckSelectCount(cardsWithFunc, cards, min, max); + } + } + } + } + if (fusionTarget.IsCode(CardId.AlbaLenatusTheAbyssDragon)) + { + // select non-albaz + if (selectedFusionMaterial.Count > 0) + { + funcList = new List> + { + (c) => c.IsFaceup() && c.Location == CardLocation.Removed, + (c) => c.Location == CardLocation.Grave && !CheckWhetherShouldKeepInGrave(c), + (c) => c.IsCode(CardId.LubellionTheSearingDragon), + }; + } + } + foreach (Func func in funcList) + { + List cardsWithFunc = cards.Where(c => func(c)).OrderBy(c => c.GetDefensePower()).ToList(); + if (cardsWithFunc.Count > 0) + { + selectedFusionMaterial.Add(cardsWithFunc[0]); + return Util.CheckSelectCount(cardsWithFunc, cards, min, max); + } + } + if (fusionTarget.IsOriginalCode(CardId.AlbaLenatusTheAbyssDragon) && cancelable) + { + return null; + } + } + break; + + // for granguignol + case CardId.GranguignolTheDuskDragon: + if (hint == HintMsg.ToGrave) + { + GranguignolTheDuskDragonSendToGYTarget(cards, out ClientCard target); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + if (hint == HintMsg.SpSummon) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.DespianQuaeritis, () => Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && c.Attack >= Util.GetBestPower(Bot) && !(c.HasType(CardType.Fusion) && c.Level >= 8)) }, + {CardId.GuidingQuemTheVirtuous, () => Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon) || Util.GetOneEnemyBetterThanValue(1500) == null }, + {CardId.DespianLuluwalilith, () => !(Duel.Player == 0 && Bot.HasInHandOrInSpellZone(CardId.BrandedFusion)) } + }; + + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = cards.FirstOrDefault(c => c.IsOriginalCode(pair.Key)); + if (target != null && pair.Value()) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for Quaeritis + case CardId.DespianQuaeritis: + { + Dictionary> checkDict = new Dictionary> + { + {CardId.FallenOfAlbaz, () => CheckAlbazFusion() }, + {CardId.GuidingQuemTheVirtuous, () => !DefaultCheckWhetherCardIdIsNegated(CardId.GuidingQuemTheVirtuous) }, + {CardId.AluberTheJesterOfDespia, () => !DefaultCheckWhetherCardIdIsNegated(CardId.AluberTheJesterOfDespia) }, + {CardId.DespianTragedy, () => true } + }; + + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for irondash dragon + case CardId.SprindTheIrondashDragon: + { + Dictionary> checkDict = new Dictionary> + { + {CardId.FallenOfAlbaz, () => CheckAlbazFusion() }, + {CardId.SpringansKitt, () => !DefaultCheckWhetherCardIdIsNegated(CardId.SpringansKitt) } + }; + + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for ash dragon + case CardId.TitanikladTheAshDragon: + { + Dictionary> checkDict = new Dictionary> + { + {CardId.FallenOfAlbaz, () => CheckAlbazFusion() }, + {CardId.GuidingQuemTheVirtuous, () => !DefaultCheckWhetherCardIdIsNegated(CardId.GuidingQuemTheVirtuous) } + }; + + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for rindrumm + case CardId.RindbrummTheStrikingDragon: + if (hint == HintMsg.SpSummon) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.FallenOfAlbaz, () => CheckAlbazFusion() }, + {CardId.RindbrummTheStrikingDragon, () => true }, + {CardId.AlbionTheShroudedDragon, () => true } + }; + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = cards.FirstOrDefault(c => c.IsOriginalCode(pair.Key)); + if (target != null && pair.Value()) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + if (hint == HintMsg.ReturnToHand) + { + List problemList = GetProblematicEnemyCardList(false, true, CardType.Monster).Intersect(cards).OrderByDescending(c => c.GetDefensePower()).ToList(); + if (problemList.Count > 0) + { + return Util.CheckSelectCount(problemList, cards, min, max); + } + ClientCard worstBotMonster = Util.GetWorstBotMonster(); + int worstBotPower = worstBotMonster == null ? 0 : worstBotMonster.GetDefensePower(); + List dangerList = cards.Where(c => c.IsFaceup() && c.Controller == 1 && c.GetDefensePower() > worstBotPower).OrderByDescending(c => c.GetDefensePower()).ToList(); + if (dangerList.Count > 0) + { + return Util.CheckSelectCount(dangerList, cards, min, max); + } + List checkIdList = new List { CardId.AluberTheJesterOfDespia, CardId.SpringansKitt }; + foreach (int checkId in checkIdList) + { + ClientCard target = cards.FirstOrDefault(c => c.Controller == 0 && c.IsCode(checkId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + List enemyMonsterList = cards.Where(c => c.Controller == 1).OrderByDescending(c => c.GetDefensePower()).ToList(); + if (enemyMonsterList.Count > 0) + { + return Util.CheckSelectCount(enemyMonsterList, cards, min, max); + } + return Util.CheckSelectCount(cards.Where(c => c.Controller == 0).OrderByDescending(c => c.GetDefensePower()).ToList(), cards, min, max); + } + break; + + // for branded dragon + case CardId.AlbionTheBrandedDragon: + if (hint == HintMsg.SpSummon) + { + AlbionTheBrandedDragonFusionTarget(cards, out ClientCard target); + if (target != null) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + if (hint == HintMsg.FusionMaterial && fusionTarget != null) + { + if (fusionTarget.IsCode(CardId.BorreloadFuriousDragon)) + { + // select 2 dark dragon monster + foreach (CardLocation loc in new[] { CardLocation.Grave, CardLocation.Hand, CardLocation.MonsterZone }) + { + List cardsInLoc = cards.Where(c => c.Location == loc).OrderBy(c => c.GetDefensePower()).ToList(); + int banishedAlbazCount = Bot.Banished.Where(c => c.IsOriginalCode(CardId.FallenOfAlbaz)).Count(); + banishedAlbazCount += selectedFusionMaterial.Where(c => c.IsOriginalCode(CardId.FallenOfAlbaz)).Count(); + foreach (ClientCard target in cardsInLoc) + { + // keep albaz + if (target.IsOriginalCode(CardId.FallenOfAlbaz) && banishedAlbazCount > 0) + { + continue; + } + selectedFusionMaterial.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + if (fusionTarget.IsCode(CardId.DespianQuaeritis)) + { + // select despain + if (selectedFusionMaterial.Count == 0) + { + foreach (CardLocation loc in new[] { CardLocation.Grave, CardLocation.Hand, CardLocation.MonsterZone }) + { + List cardsInLoc = cards.Where(c => c.Location == loc && c.HasSetcode(SetcodeDespain) && (loc != CardLocation.Grave || !CheckWhetherShouldKeepInGrave(c))) + .OrderBy(c => c.GetDefensePower()).ToList(); + if (cardsInLoc.Count > 0) + { + selectedFusionMaterial.Add(cardsInLoc[0]); + return Util.CheckSelectCount(cardsInLoc, cards, min, max); + } + } + } + // select light/dark + else + { + foreach (CardLocation loc in new[] { CardLocation.Grave, CardLocation.Hand, CardLocation.MonsterZone }) + { + List cardsInLoc = cards.Where(c => c.Location == loc && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark) && (loc != CardLocation.Grave || !CheckWhetherShouldKeepInGrave(c))) + .OrderBy(c => c.GetDefensePower()).ToList(); + if (cardsInLoc.Count > 0) + { + if (!activatedCardIdList.Contains(CardId.TriBrigadeMercourier + 1)) + { + ClientCard mercourier = cardsInLoc.FirstOrDefault(c => c.IsCode(CardId.TriBrigadeMercourier)); + if (mercourier != null) + { + selectedFusionMaterial.Add(mercourier); + return Util.CheckSelectCount(new List { mercourier }, cards, min, max); + } + } + if (!activatedCardIdList.Contains(CardId.DespianTragedy) && CheckRemainInDeck(CardId.AluberTheJesterOfDespia, CardId.GuidingQuemTheVirtuous) > 0) + { + ClientCard tragedy = cardsInLoc.FirstOrDefault(c => c.IsCode(CardId.DespianTragedy)); + if (tragedy != null) + { + selectedFusionMaterial.Add(tragedy); + return Util.CheckSelectCount(new List { tragedy }, cards, min, max); + } + } + + selectedFusionMaterial.Add(cardsInLoc[0]); + return Util.CheckSelectCount(cardsInLoc, cards, min, max); + } + } + } + } + if (fusionTarget.IsCode(albazFusionMonster)) + { + // selecting albaz + if (selectedFusionMaterial.Count == 0) + { + foreach (CardLocation loc in new[] { CardLocation.Grave, CardLocation.MonsterZone, CardLocation.Hand }) + { + ClientCard albaz = cards.Where(c => c.IsCode(CardId.FallenOfAlbaz) && c.Location == loc).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (albaz != null) + { + selectedFusionMaterial.Add(albaz); + return Util.CheckSelectCount(new List { albaz }, cards, min, max); + } + } + } + else + { + if (fusionTarget.IsOriginalCode(CardId.AlbaLenatusTheAbyssDragon) && cancelable) + { + return null; + } + List> funcList = new List> + { + (c) => c.Location == CardLocation.Grave && !CheckWhetherShouldKeepInGrave(c), + (c) => c.Location == CardLocation.MonsterZone && c.GetDefensePower() <= 2000, + (c) => c.Location == CardLocation.Grave, + (c) => c.Location == CardLocation.Hand, + (c) => c.Location == CardLocation.MonsterZone + }; + foreach (Func func in funcList) + { + List targetList = cards.Where(c => func(c)).OrderBy(c => c.GetDefensePower()).ToList(); + if (targetList.Count > 0) + { + selectedFusionMaterial.Add(targetList[0]); + return Util.CheckSelectCount(new List { targetList[0] }, cards, min, max); + } + } + } + } + } + if (hint == HintMsg.OperateCard) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.BrandedInHighSpirits, () => Bot.HasInMonstersZone(CardId.GuidingQuemTheVirtuous) && BrandedInHighSpiritsActivateCheck() }, + {CardId.BrightestBlazingBrandedKing, () => Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.IsCode(albazFusionMonster)) }, + {CardId.BrandedInRed, () => Bot.Graveyard.Any(c => c != null && (c.HasSetcode(SetcodeDespain) || c.IsCode(CardId.FallenOfAlbaz))) }, + {CardId.BrandedRetribution, () => Bot.Graveyard.Where(c => c != null && c.IsCode(albazFusionMonster)).Count() > 1 }, + {CardId.BrandedFusion, () => CheckRemainInDeck(CardId.FallenOfAlbaz) > 0 }, + {CardId.BrandedBeast, () => Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeBystial)) }, + {CardId.BrandedLost, () => true }, + {CardId.BrandedInWhite, () => true } + }; + + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = cards.FirstOrDefault(c => c.IsOriginalCode(pair.Key)); + if (target != null && pair.Value()) + { + fusionTarget = target; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + + // for luluwa + case CardId.DespianLuluwalilith: + if (hint == HintMsg.Disable) + { + List enemyCardList = cards.Where(c => c.IsFaceup() && c.Controller == 1).ToList(); + List problemCardList = GetProblematicEnemyCardList(false, false, 0).Intersect(enemyCardList).ToList(); + if (problemCardList.Count > 0) + { + return Util.CheckSelectCount(ShuffleList(problemCardList), cards, min, max); + } + List monsterList = GetMonsterListForTargetNegate(false, 0).Intersect(enemyCardList).ToList(); + if (monsterList.Count > 0) + { + return Util.CheckSelectCount(ShuffleList(monsterList), cards, min, max); + } + if (enemyCardList.Count > 0) + { + return Util.CheckSelectCount(ShuffleList(enemyCardList), cards, min, max); + } + } + if (hint == HintMsg.SpSummon) + { + foreach (CardLocation loc in new[] { CardLocation.Deck, CardLocation.Hand }) + { + foreach (int checkId in new List { CardId.BlazingCartesiaTheVirtuous, CardId.GuidingQuemTheVirtuous }) + { + ClientCard target = cards.FirstOrDefault(c => c.Location == loc && c.IsOriginalCode(checkId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + } + break; + + default: + break; + } + } + + // drop 1 hand + bool discardHand = hint == HintMsg.Discard; + bool handToDeck = hint == HintMsg.ToDeck && cards.All(c => c.Location == CardLocation.Hand); + if (min == 1 && max == 1 && (discardHand || handToDeck)) + { + if (currentSolvingChain != null && currentSolvingChain.IsCode(CardId.BrandedOpening)) + { + ClientCard tragedy = cards.FirstOrDefault(card => card.IsCode(CardId.DespianTragedy)); + if (tragedy != null) + { + return Util.CheckSelectCount(new List { tragedy }, cards, min, max); + } + } + if (discardHand) + { + // discard activating shrouded + foreach (ClientCard target in cards) + { + if (target.IsCode(CardId.AlbionTheShroudedDragon) && Duel.CurrentChain.Contains(target)) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + + List discardList = new List { + CardId.BrandedRetribution, CardId.AlbionTheShroudedDragon, CardId.BystialSaronir, CardId.BrightestBlazingBrandedKing, + CardId.BrandedInHighSpirits, CardId.BlazingCartesiaTheVirtuous, CardId.DespianTragedy }; + foreach (int id in discardList) + { + ClientCard card = cards.FirstOrDefault(c => c.IsCode(id)); + if (card != null) + { + return Util.CheckSelectCount(new List { card }, cards, min, max); + } + } + } + // return dump card + foreach (ClientCard card in cards) + { + if (cards.Where(c => c.IsCode(card.Id)).Count() > 1) + { + return Util.CheckSelectCount(new List { card }, cards, min, max); + } + } + List improperCardIdList = new List + { + CardId.BrandedRetribution, CardId.BrandedInHighSpirits, CardId.DespianTragedy, CardId.FusionDeployment, CardId.BrandedBeast, + CardId.AlbionTheShroudedDragon, CardId.BrandedOpening, CardId.GoldSarcophagus, CardId.FoolishBurial, CardId.FallenOfAlbaz, + CardId.BrandedInRed, _CardId.InfiniteImpermanence, CardId.TheBystialLubellion, CardId.BrandedLost, CardId.SpringansKitt, + CardId.GuidingQuemTheVirtuous, _CardId.CrossoutDesignator, _CardId.CalledByTheGrave, CardId.TriBrigadeMercourier, _CardId.AshBlossom, _CardId.MaxxC + }; + foreach (int id in improperCardIdList) + { + if (id == CardId.BrandedLost && Bot.HasInHand(CardId.BrandedFusion) && BrandedFusionActivateCheck()) continue; + ClientCard target = cards.FirstOrDefault(c => c.IsCode(id)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + else if (discardHand && min > 0 && min == max) + { + List discardList = new List(); + List graveEffectIdList = new List { CardId.AlbionTheShroudedDragon, CardId.BrandedRetribution, CardId.BrandedInHighSpirits, + CardId.BrightestBlazingBrandedKing, CardId.DespianTragedy }; + discardList.AddRange(ShuffleList(cards.Where(c => c.IsCode(graveEffectIdList)).ToList())); + List remainHandList = cards.Except(discardList).ToList(); + + HashSet seenIds = new HashSet(); + for (int idx = remainHandList.Count - 1; idx >= 0; idx --) + { + ClientCard currentCard = remainHandList[idx]; + if (!seenIds.Add(currentCard.Id)) + { + discardList.Add(currentCard); + remainHandList.Remove(currentCard); + } + } + + List improperCardIdList = new List + { + CardId.FusionDeployment, CardId.BrandedBeast, CardId.AlbionTheShroudedDragon, CardId.BrandedOpening, CardId.GoldSarcophagus, + CardId.FoolishBurial, CardId.FallenOfAlbaz, CardId.BrandedInRed, _CardId.InfiniteImpermanence, CardId.TheBystialLubellion, + CardId.BrandedLost, CardId.SpringansKitt, CardId.GuidingQuemTheVirtuous, _CardId.CrossoutDesignator, _CardId.CalledByTheGrave, + CardId.TriBrigadeMercourier, _CardId.AshBlossom, _CardId.MaxxC + }; + foreach (int id in improperCardIdList) + { + ClientCard target = remainHandList.FirstOrDefault(c => c.IsCode(id)); + if (target != null) + { + discardList.Add(target); + } + } + if (discardList.Count > min) + { + discardList = discardList.Take(min).ToList(); + } + + return Util.CheckSelectCount(discardList, cards, min, max); + } + + // for The Bystial Lubellion + if (theBystialLubellionSelecting) + { + theBystialLubellionSelecting = false; + ClientCard target = TheBystialLubellionSpSummonCost(cards); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } else + { + List targetList = new List(cards); + targetList.Sort(CardContainer.CompareCardAttack); + return Util.CheckSelectCount(targetList, cards, min, max); + } + } + + // for shrouded/saronir + if (albionTheShroudedDragonSelecting || (currentSolvingChain != null && currentSolvingChain.IsCode(CardId.BystialSaronir))) + { + // send retribution first + ClientCard retribution = cards.FirstOrDefault(c => c.IsCode(CardId.BrandedRetribution)); + if (retribution != null) + { + if (retribution.Location == CardLocation.Deck || Bot.GetGraveyardMonsters().Where(c => c.IsCode(albazFusionMonster)).Count() < 2) + { + return Util.CheckSelectCount(new List { retribution }, cards, min, max); + } + } + // send spells to recycle + if (Bot.HasInGraveyard(CardId.BrandedRetribution) || (Bot.HasInGraveyard(CardId.DespianTragedy) && !activatedCardIdList.Contains(CardId.DespianTragedy))) + { + Dictionary> deckCheckDict = new Dictionary>{ + {CardId.BrandedFusion, () => BrandedFusionActivateCheck()}, + {CardId.BrandedLost, () => { + if (Duel.Player == 0 && Duel.Phase >= DuelPhase.End) return false; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedFusion) && BrandedFusionActivateCheck()) return true; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedInWhite) && BrandedInWhiteActivateCheck()) return true; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedInRed) && BrandedInRedActivateCheck() != null) return true; + if (!summoned && Bot.HasInHand(CardId.FallenOfAlbaz) && CheckAlbazFusion()) return true; + if ((Bot.HasInMonstersZone(CardId.BlazingCartesiaTheVirtuous) || (!summoned && Bot.HasInHand(CardId.BlazingCartesiaTheVirtuous)))) return true; + return false; + } }, + {CardId.BrandedInHighSpirits, BrandedInHighSpiritsActivateCheck}, + {CardId.BrandedInRed, () => BrandedInRedActivateCheck() != null }, + {CardId.BrandedInWhite, BrandedInWhiteActivateCheck }, + {CardId.BrandedRetribution, () => cards.Any(c => c.IsCode(CardId.BrandedRetribution) && c.Location == CardLocation.Removed) }, + {CardId.BrightestBlazingBrandedKing, () => Bot.GetMonsters().Any(c => c.IsFaceup() && c.IsCode(albazFusionMonster)) }, + {CardId.BrandedOpening, () => Bot.Hand.Count > 2 } + }; + foreach (KeyValuePair> pair in deckCheckDict) + { + ClientCard target = cards.FirstOrDefault(card => card.Location == CardLocation.Deck && card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + + // for abyss dragon + if (albionTheShroudedDragonSelecting && + FallenOfAlbazSetCheck() && (summoned || !Bot.HasInHand(new List { CardId.FallenOfAlbaz, CardId.BrandedInHighSpirits }))) + { + List checkIdList = new List { + CardId.BrandedRetribution, CardId.BrandedInHighSpirits, CardId.BrightestBlazingBrandedKing, CardId.BrandedInWhite, CardId.BrandedOpening, + CardId.BrandedInRed, CardId.BrandedBeast, CardId.BrandedLost + }; + if (!BrandedFusionActivateCheck()) + { + checkIdList.Add(CardId.BrandedFusion); + } + foreach (int checkId in checkIdList) + { + ClientCard target = cards.FirstOrDefault(c => c.Id == checkId); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + + // send from deck + List checkList = new List { CardId.BrandedInHighSpirits, CardId.BrandedOpening, CardId.BrightestBlazingBrandedKing, CardId.BrandedBeast, CardId.BrandedLost }; + foreach (int checkId in checkList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(checkId) && c.Location == CardLocation.Deck); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + return Util.CheckSelectCount(ShuffleList(new List(cards)), cards, min, max); + } + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + public override int OnSelectOption(IList options) + { + ChainInfo currentSolvingChain = Duel.GetCurrentSolvingChainInfo(); + if (currentSolvingChain != null) + { + // 1190=Add to Hand, 1152=Special Summon + if (options.Count == 2 && options.Contains(1190) && options.Contains(1152)) + { + if (currentSolvingChain.IsCode(CardId.BrandedOpening)) + { + return (CheckShouldNoMoreSpSummon() && !summoned && Duel.Player == 0) ? options.IndexOf(1190) : options.IndexOf(1152); + } + + if (fusionTarget != null && ( + currentSolvingChain.IsCode(CardId.DespianQuaeritis) + || currentSolvingChain.IsCode(CardId.TitanikladTheAshDragon) + || currentSolvingChain.IsCode(CardId.SprindTheIrondashDragon) + )) + { + if (fusionTarget.IsCode(CardId.FallenOfAlbaz)) + { + return CheckAlbazFusion() ? options.IndexOf(1152) : options.IndexOf(1190); + } + if (fusionTarget.IsCode(CardId.GuidingQuemTheVirtuous, CardId.SpringansKitt)) + { + return CheckShouldNoMoreSpSummon() ? options.IndexOf(1190) : options.IndexOf(1152); + } + if (fusionTarget.IsCode(CardId.AluberTheJesterOfDespia)) + { + return activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) ? options.IndexOf(1190) : options.IndexOf(1152); + } + return (CheckShouldNoMoreSpSummon() && !summoned) ? options.IndexOf(1190) : options.IndexOf(1152); + } + } + + // 1190=Add to Hand, 1153=Set + if (currentSolvingChain.IsCode(CardId.AlbionTheBrandedDragon) && fusionTarget != null) + { + if (fusionTarget.IsOriginalCode(CardId.BrandedInHighSpirits) && Duel.Player == 0) + { + return BrandedInHighSpiritsActivateCheck() ? options.IndexOf(1190) : options.IndexOf(1153); + } + if (fusionTarget.IsOriginalCode(CardId.BrandedInRed) && Duel.Player == 0) + { + if (nadirActivated) return options.IndexOf(1153); + return BrandedInRedActivateCheck() != null ? options.IndexOf(1190) : options.IndexOf(1153); + } + if (fusionTarget.Data != null) + { + bool setFlag = fusionTarget.Data.HasType(CardType.Trap); + setFlag |= fusionTarget.Data.HasType(CardType.QuickPlay) && Duel.Player == 0; + setFlag |= Bot.Hand.Count >= 6 && Duel.Player == 0; + return setFlag ? options.IndexOf(1153) : options.IndexOf(1190); + } + } + } + + return base.OnSelectOption(options); + } + + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + ChainInfo currentSovingChain = Duel.GetCurrentSolvingChainInfo(); + if (currentSovingChain != null && currentSovingChain.ActivatePlayer == 0 && currentSovingChain.IsCode(CardId.SprindTheIrondashDragon)) + { + return SprindTheIrondashDragonMoveZone(available, null); + } + + if (player == 0 && location == CardLocation.MonsterZone) + { + List zoneIdList = ShuffleList(new List { 5, 6 }); + zoneIdList.AddRange(ShuffleList(new List { 0, 2, 4 })); + zoneIdList.AddRange(ShuffleList(new List { 1, 3 })); + foreach (int zoneId in zoneIdList) + { + int zone = (int)System.Math.Pow(2, zoneId); + if ((available & zone) != 0 && Bot.MonsterZone[zoneId] == null) + { + return zone; + } + } + } + return base.OnSelectPlace(cardId, player, location, available); + } + + public override bool OnSelectYesNo(long desc) + { + if (desc == Util.GetStringId(CardId.BrandedInHighSpirits, 2)) + { + if (CheckWhetherWillbeRemoved()) return false; + if (fusionTarget != null && fusionTarget.IsOriginalCode(CardId.TriBrigadeMercourier)) + { + return !Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.IsCode(albazFusionMonster)); + } + } + + if (desc == Util.GetStringId(CardId.RindbrummTheStrikingDragon, 2)) + { + bool checkFlag = Enemy.MonsterZone.Any(c => c != null); + checkFlag |= Bot.MonsterZone.Any(c => c != null && (c.IsOriginalCode(CardId.AluberTheJesterOfDespia) || c.IsOriginalCode(CardId.SpringansKitt))); + return checkFlag; + } + + if (desc == Util.GetStringId(CardId.DespianLuluwalilith, 2)) + { + bool checkFlag = Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && !c.IsDisabled()); + checkFlag |= Enemy.SpellZone.Any(c => c != null && c.IsFaceup() && !c.IsDisabled()); + return checkFlag; + } + + if (desc == Util.GetStringId(CardId.BrandedInRed, 0)) + { + // fix material list + brandedInRedMaterialList = brandedInRedMaterialList.Where(c => c != null && (c.Location == CardLocation.MonsterZone || c.Location == CardLocation.Hand)).ToList(); + + List materialList = Bot.MonsterZone.Where(c => c != null && c.Attack <= 2500 && !c.IsCode(cannotBeFusionMaterialIdList)).ToList(); + materialList.AddRange(Bot.Hand.Where(c => c.IsMonster() + && !(CheckWhetherCanSummon() && + ((!activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && c.IsCode(CardId.AluberTheJesterOfDespia)) + || (!activatedCardIdList.Contains(CardId.SpringansKitt) && c.IsCode(CardId.SpringansKitt))) + ) + ) + ); + + BrandedInRedFusionCheck(Bot.ExtraDeck, 0, + materialList, brandedInRedMaterialList, + out ClientCard _fusionTarget, out _); + + return _fusionTarget != null; + } + + if (desc == Util.GetStringId(CardId.SprindTheIrondashDragon, 2)) + { + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (currentSolvingChain != null) + { + int value = SprindTheIrondashDragonDestroyValue(currentSolvingChain.Sequence); + return value > 0; + } + } + + return base.OnSelectYesNo(desc); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (currentSolvingChain != null && currentSolvingChain.IsCode(CardId.AlbionTheSanctifireDragon)) + { + sanctifireSelectPositionCount++; + if (sanctifireSelectPositionCount >= 2) + { + if (Duel.Phase <= DuelPhase.Main2) + { + return CardPosition.FaceUpDefence; + } + } + } + + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2) + { + bool turnDefense = false; + if (cardData.Attack <= cardData.Defense) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } + int cardAttack = cardData.Attack; + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardAttack); + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + public override void OnNewTurn() + { + if (Duel.Turn <= 1) + { + dimensionShifterCount = 0; + } + + summoned = false; + enemyActivateMaxxC = false; + enemyActivateLockBird = false; + enemyActivateInfiniteImpermanenceFromHand = false; + nadirActivated = false; + fusionToGYFlag = false; + spSummoningAlbaz = false; + cartesiaSummonGoal = 0; + sanctifireSelectPositionCount = 0; + quemSummonFlag = 0; + if (dimensionShifterCount > 0) dimensionShifterCount--; + cartesiaMaterialList.Clear(); + brandedInRedMaterialList.Clear(); + infiniteImpermanenceList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + sendToGYThisTurn.Clear(); + activatedCardIdList.Clear(); + enemyPlaceThisTurn.Clear(); + base.OnNewTurn(); + } + + public override void OnChaining(int player, ClientCard card) + { + Duel.LastChainTargets.Clear(); + if (card == null) return; + + if (player == 1) + { + if (card.IsCode(_CardId.InfiniteImpermanence)) + { + if (enemyActivateInfiniteImpermanenceFromHand) + { + enemyActivateInfiniteImpermanenceFromHand = false; + } + else + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + } + } + base.OnChaining(player, card); + } + + public override void OnChainSolved(int chainIndex) + { + ChainInfo currentCard = Duel.GetCurrentSolvingChainInfo(); + if (currentCard != null) + { + // if activation is negated, it can activate again. + if (currentCard.ActivatePlayer == 0) + { + List activateCheck = new List { CardId.NadirServant, CardId.FusionDeployment, CardId.BrandedFusion, CardId.BrandedInRed }; + if (currentCard.IsCode(activateCheck)) + { + activatedCardIdList.Add(currentCard.ActivateId); + } + } + if (!Duel.IsCurrentSolvingChainNegated()) + { + if (currentCard.ActivatePlayer == 1) + { + if (currentCard.IsCode(_CardId.MaxxC)) + enemyActivateMaxxC = true; + if (currentCard.IsCode(_CardId.LockBird)) + enemyActivateLockBird = true; + if (currentCard.IsCode(CardId.DimensionShifter)) + dimensionShifterCount = 2; + } + if (currentCard.ActivatePlayer == 0 && currentCard.IsCode(CardId.NadirServant)) + { + nadirActivated = true; + } + } + } + fusionTarget = null; + selectedFusionMaterial.Clear(); + sanctifireSelectPositionCount = 0; + + base.OnChainSolved(chainIndex); + } + + public override void OnChainEnd() + { + cartesiaSummonGoal = 0; + cartesiaMaterialList.Clear(); + brandedInRedMaterialList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + enemyActivateInfiniteImpermanenceFromHand = false; + theBystialLubellionSelecting = false; + albionTheShroudedDragonSelecting = false; + spSummoningAlbaz = false; + for (int idx = enemyPlaceThisTurn.Count - 1; idx >= 0; idx--) + { + ClientCard checkTarget = enemyPlaceThisTurn[idx]; + if (checkTarget == null || (checkTarget.Location != CardLocation.SpellZone && checkTarget.Location != CardLocation.MonsterZone)) + { + enemyPlaceThisTurn.RemoveAt(idx); + } + } + if (quemSummonFlag > 0) quemSummonFlag--; + base.OnChainEnd(); + } + + public override void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + if (previousControler == 1) + { + if (card != null) + { + if (card.IsCode(_CardId.InfiniteImpermanence) && previousLocation == (int)CardLocation.Hand && currentLocation == (int)CardLocation.SpellZone) + enemyActivateInfiniteImpermanenceFromHand = true; + } + } + if (card != null) + { + if (currentControler == 1 && (currentLocation == (int)CardLocation.MonsterZone || currentLocation == (int)CardLocation.SpellZone)) + { + enemyPlaceThisTurn.Add(card); + } + if (currentControler == 0) + { + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (previousLocation == (int)CardLocation.Grave && currentLocation != (int)CardLocation.Grave) + { + sendToGYThisTurn.Remove(card); + } + if (currentLocation == (int)CardLocation.Grave) + { + if (card.HasType(CardType.Fusion)) fusionToGYFlag = true; + sendToGYThisTurn.Add(card); + } + if (currentLocation == (int)CardLocation.MonsterZone && card != null && card.IsCode(CardId.GuidingQuemTheVirtuous)) + { + quemSummonFlag = 2; + } + } + } + + base.OnMove(card, previousControler, previousLocation, currentControler, currentLocation); + } + + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public bool TheBystialLubellionSpSummon() + { + if (Card.Location == CardLocation.Hand) + { + return false; + } + ClientCard costTarget = TheBystialLubellionSpSummonCost(Bot.GetMonsters()); + if (costTarget != null) + { + theBystialLubellionSelecting = true; + activatedCardIdList.Add(Card.Id - 1); + return true; + } + return false; + } + + public ClientCard TheBystialLubellionSpSummonCost(IList costList) + { + Dictionary> checkDict = new Dictionary>{ + {CardId.AlbionTheBrandedDragon, (card) => sendToGYThisTurn.All(c => !c.IsCode(CardId.AlbionTheBrandedDragon))}, + {CardId.BystialSaronir, (card) => !activatedCardIdList.Contains(CardId.BystialSaronir + 1) && !CheckWhetherWillbeRemoved() }, + {CardId.TitanikladTheAshDragon, (card) => Util.IsTurn1OrMain2() || card.GetDefensePower() < 2500 }, + {CardId.AlbaLenatusTheAbyssDragon, (card) => Util.IsTurn1OrMain2() || card.IsDisabled() || card.GetDefensePower() < 2500 }, + {CardId.AlbionTheShroudedDragon, (card) => Util.IsTurn1OrMain2() || card.GetDefensePower() < 2500 }, + {CardId.BorreloadFuriousDragon, (card) => card.IsDisabled() && CheckRemainInDeck(CardId.BrandedBeast, CardId.BrandedLost) > 0 }, + }; + + foreach (KeyValuePair> pair in checkDict) + { + List targetList = costList.Where(card => card.IsCode(pair.Key)).ToList(); + foreach (ClientCard target in targetList) + { + if (target != null && pair.Value(target)) + { + return target; + } + } + } + return null; + } + + public bool TheBystialLubellionActivate() + { + if (CheckWhetherNegated(true, Card.Location == CardLocation.MonsterZone, CardType.Monster)) return false; + if (Card.Location == CardLocation.Hand) + { + activatedCardIdList.Add(Card.Id); + } else + { + activatedCardIdList.Add(Card.Id + 1); + } + return true; + } + + public bool AlbionTheShroudedDragonActivate() + { + if (CheckWhetherNegated(true, false, CardType.Monster) || CheckWhetherWillbeRemoved()) return false; + bool checkFlag = CheckRemainInDeck(CardId.BrandedRetribution, CardId.BrandedOpening, CardId.BrightestBlazingBrandedKing, CardId.BrandedInHighSpirits) > 0; + if (Bot.HasInGraveyard(CardId.BrandedRetribution)) + { + checkFlag |= CheckRemainInDeck(CardId.BrandedFusion, CardId.BrandedBeast, CardId.BrandedInRed, CardId.BrandedInWhite, CardId.BrandedLost) > 0; + } + if (Bot.HasInSpellZone(CardId.BrandedBeast)) + { + checkFlag |= CheckRemainInDeck(CardId.BrandedLost) > 0; + } + if (Card.Location == CardLocation.Grave) + { + checkFlag |= CheckRemainInDeck(CardId.BrandedInWhite) > 0; + } + // for abyss dragon + if (FallenOfAlbazSetCheck() && (summoned || !Bot.HasInHand(new List { CardId.FallenOfAlbaz, CardId.BrandedInHighSpirits }))) + { + checkFlag |= Bot.HasInHand(new List { CardId.BrandedBeast, CardId.BrandedInHighSpirits, CardId.BrandedInWhite, CardId.BrandedInRed, CardId.BrandedLost, CardId.BrandedOpening, CardId.BrandedRetribution, CardId.BrightestBlazingBrandedKing }); + } + + if (checkFlag) + { + activatedCardIdList.Add(Card.Id); + albionTheShroudedDragonSelecting = true; + return true; + } + return false; + } + + public bool BystialSaronirActivate() + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + // banish & spsummon + if (Card.Location == CardLocation.Hand) + { + // banish enemy target + if (Util.GetLastChainCard() != null && Duel.LastChainPlayer == 1) + { + List chainTargetList = Duel.LastChainTargets.Where(c => CheckBystialCanBanish(c)).ToList(); + if (chainTargetList.Count > 0) + { + AI.SelectCard(chainTargetList); + currentDestroyCardList.Add(chainTargetList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + List enemyChainList = Duel.CurrentChain.Where(c => c != null && c.Controller == 1 && CheckBystialCanBanish(c) && !currentDestroyCardList.Contains(c)) + .OrderByDescending(c => c.GetDefensePower()).ToList(); + if (enemyChainList.Count > 0) + { + AI.SelectCard(enemyChainList); + currentDestroyCardList.Add(enemyChainList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + + // banish cards with effect + if (!CheckShouldNoMoreSpSummon()) + { + ClientCard mercourier = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsCode(CardId.TriBrigadeMercourier)); + if (mercourier != null && !activatedCardIdList.Contains(CardId.TriBrigadeMercourier + 1)) + { + AI.SelectCard(mercourier); + currentDestroyCardList.Add(mercourier); + activatedCardIdList.Add(Card.Id); + return true; + } + ClientCard tragedy = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsCode(CardId.DespianTragedy)); + if (tragedy != null && !activatedCardIdList.Contains(CardId.DespianTragedy)) + { + AI.SelectCard(tragedy); + currentDestroyCardList.Add(tragedy); + activatedCardIdList.Add(Card.Id); + return true; + } + if (Bot.HasInGraveyard(CardId.TheBystialLubellion) && !activatedCardIdList.Contains(CardId.TheBystialLubellion) + && Duel.Player == 0 && CheckRemainInDeck(CardId.BrandedLost, CardId.BrandedBeast) > 0 + && CurrentTiming == -1) + { + List targetList = Enemy.Graveyard.Where(c => c != null && CheckBystialCanBanish(c)).OrderByDescending(card => card.Attack).ToList(); + targetList.AddRange(Bot.Graveyard + .Where(c => c != null && CheckBystialCanBanish(c) && !c.IsCode(CardId.TheBystialLubellion) && !CheckWhetherShouldKeepInGrave(c)) + .OrderBy(card => card.Attack).ToList() + ); + if (targetList.Count > 0) + { + AI.SelectCard(targetList); + currentDestroyCardList.Add(targetList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + // defense + if (Bot.UnderAttack && Bot.BattlingMonster == null) + { + List targetList = Enemy.Graveyard.Where(c => CheckBystialCanBanish(c)).OrderByDescending(c => c.GetDefensePower()).ToList(); + targetList.AddRange(Bot.Graveyard.Where(c => CheckBystialCanBanish(c)).OrderBy(c => c.GetDefensePower())); + if (targetList.Count > 0) + { + AI.SelectCard(targetList); + currentDestroyCardList.Add(targetList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + // trigger beast + if (Duel.Player == 1 && (Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) + && !activatedCardIdList.Contains(CardId.BrandedBeast) && Bot.SpellZone.Any(c => c != null && c.IsCode(CardId.BrandedBeast) && (c.IsFacedown() || !c.IsDisabled())) + && !Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeBystial))) + { + List dangerList = GetProblematicEnemyCardList(true, false, CardType.Trap); + if (dangerList.Count > 0) + { + List targetList = Enemy.Graveyard.Where(c => CheckBystialCanBanish(c)).OrderByDescending(c => c.GetDefensePower()).ToList(); + targetList.AddRange(Bot.Graveyard.Where(c => CheckBystialCanBanish(c)).OrderBy(c => c.GetDefensePower())); + if (targetList.Count > 0) + { + AI.SelectCard(targetList); + currentDestroyCardList.Add(targetList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + } + // send to GY + if (Card.Location == CardLocation.Grave && !CheckWhetherWillbeRemoved()) + { + if (Bot.HasInGraveyard(CardId.BrandedRetribution)) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } else if (CheckRemainInDeck(CardId.TheBystialLubellion, CardId.BrandedRetribution, CardId.BrandedInHighSpirits, CardId.BrightestBlazingBrandedKing, CardId.BrandedOpening) > 0) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + return false; + } + + public bool CheckBystialCanBanish(ClientCard c) + { + return c != null && c.Location == CardLocation.Grave && c.IsMonster() && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark); + } + + public bool AluberTheJesterOfDespiaSummon() + { + if (CheckWhetherNegated(true, true, CardType.Monster) || enemyActivateLockBird || activatedCardIdList.Contains(Card.Id)) return false; + summoned = true; + return true; + } + + public bool AluberTheJesterOfDespiaActivate() + { + // search + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + // spsummon + else + { + List targetCardList = GetMonsterListForTargetNegate(true, CardType.Monster); + ClientCard lastChainCard = Util.GetLastChainCard(); + // chain to protect + if (lastChainCard != null && lastChainCard.Controller == 0) + { + AI.SelectCard(targetCardList); + activatedCardIdList.Add(Card.Id); + return true; + } + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + AI.SelectCard(targetCardList); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + public bool FallenOfAlbazSummon() + { + if (CheckAlbazFusion(Card)) + { + summoned = true; + return true; + } + return false; + } + + public bool FallenOfAlbazSet() + { + if (FallenOfAlbazSetCheck()) + { + summoned = true; + return true; + } + return false; + } + + public bool FallenOfAlbazSetCheck() + { + if (!Bot.HasInExtra(CardId.AlbaLenatusTheAbyssDragon) || nadirActivated) return false; + // check dangerous dragon + if (!Bot.HasInSpellZone(CardId.BrandedLost, true, true) || Bot.GetHandCount() < 2) + { + foreach (int dangerId in dangerousDragonIdList) + { + if (Enemy.HasInMonstersZone(dangerId, true, false, true)) + { + return true; + } + } + } + // check dragon count + int dragonCount = Enemy.GetMonsters().Where(c => c != null && c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasRace(CardRace.Dragon)).Count(); + if (dragonCount > 1) + { + return true; + } + return false; + } + + /// + /// Check whether should call albaz. + /// + /// + /// + public bool CheckAlbazFusion(ClientCard exceptCost = null) { + return CheckAlbazFusion(exceptCost, out _); + } + + public bool CheckAlbazFusion(ClientCard exceptCost, out List enemyMonsterList) + { + enemyMonsterList = null; + int costHandCount = Bot.Hand.Where(c => c != exceptCost).Count(); + if (costHandCount <= 0 || Enemy.GetMonsterCount() == 0) return false; + if (CheckWhetherNegated(true, true, CardType.Monster) || activatedCardIdList.Contains(CardId.FallenOfAlbaz) || nadirActivated) return false; + if (!Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) && !Bot.HasInSpellZone(CardId.MirrorjadeTheIcebladeDragon) + && Bot.HasInExtra(CardId.MirrorjadeTheIcebladeDragon)) + { + ClientCard target = Enemy.GetMonsters() + .Where(c => c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (target != null) + { + enemyMonsterList = new List { target }; + return true; + } + } + if (Bot.HasInExtra(CardId.AlbaLenatusTheAbyssDragon)) + { + List targetList = Enemy.GetMonsters().Where(c => c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasRace(CardRace.Dragon)) + .OrderByDescending(c => c.GetDefensePower()).ToList(); + if (targetList.Count > 0) + { + enemyMonsterList = targetList; + return true; + } + } + if (Bot.HasInExtra(CardId.AlbionTheBrandedDragon)) + { + ClientCard target = Enemy.GetMonsters() + .Where(c => c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Light)) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (target != null) + { + enemyMonsterList = new List { target }; + return true; + } + } + if (Bot.HasInExtra(CardId.AlbionTheSanctifireDragon)) + { + ClientCard target = Enemy.GetMonsters() + .Where(c => c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Light) && c.HasRace(CardRace.SpellCaster)) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (target != null) + { + enemyMonsterList = new List { target }; + return true; + } + } + if (Bot.HasInExtra(CardId.LubellionTheSearingDragon)) + { + ClientCard target = Enemy.GetMonsters() + .Where(c => c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Dark)) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (costHandCount >= 2 && target != null) + { + enemyMonsterList = new List { target }; + return true; + } + } + if (Bot.HasInExtra(CardId.BorreloadFuriousDragon)) + { + ClientCard target = Enemy.GetMonsters() + .Where(c => c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasRace(CardRace.Dragon) && c.HasAttribute(CardAttribute.Dark)) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (target != null) + { + enemyMonsterList = new List { target }; + return true; + } + } + if (Bot.HasInExtra(CardId.TitanikladTheAshDragon)) + { + ClientCard target = Enemy.GetMonsters() + .Where(c => c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.Attack >= 2500) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (target != null) + { + enemyMonsterList = new List { target }; + return true; + } + } + if (Bot.HasInExtra(CardId.RindbrummTheStrikingDragon)) + { + ClientCard target = Enemy.GetMonsters() + .Where(c => c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasRace(CardRace.Beast | CardRace.BeastWarrior | CardRace.WindBeast)) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (target != null) + { + enemyMonsterList = new List { target }; + return true; + } + } + if (Bot.HasInExtra(CardId.SprindTheIrondashDragon)) + { + ClientCard target = Enemy.GetMonsters() + .Where(c => c != null && c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && enemyPlaceThisTurn.Contains(c) && c.IsSpecialSummoned + && c.GetDefensePower() >= Util.GetBestPower(Bot)) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (target != null) + { + enemyMonsterList = new List { target }; + return true; + } + } + + return false; + } + + public bool FallenOfAlbazActivate() + { + if (Bot.HasInExtra(CardId.AlbaLenatusTheAbyssDragon) && Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && c.IsCode(dangerousDragonIdList))) return false; + if (CheckAlbazFusion()) + { + activatedCardIdList.Add(Card.Id); + return true; + } + return false; + } + + public bool SpringansKittSummon() + { + if (CheckWhetherNegated(true, true, CardType.Monster) || enemyActivateLockBird || activatedCardIdList.Contains(Card.Id + 1)) return false; + summoned = true; + return true; + } + + public bool SpringansKittActivate() + { + // spsummon + if (Card.Location == CardLocation.Hand) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + if (CheckShouldNoMoreSpSummon()) + { + bool skipFlag = !summoned; + skipFlag |= activatedCardIdList.Contains(CardId.BrandedFusion); + skipFlag |= Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)); + if (skipFlag) + { + return false; + } + } + activatedCardIdList.Add(Card.Id); + return true; + } + // search + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool GuidingQuemTheVirtuousSummon() + { + if (CheckWhetherNegated(true, true, CardType.Monster) || CheckWhetherWillbeRemoved()) return false; + if (activatedCardIdList.Contains(Card.Id)) return false; + summoned = true; + return true; + } + + public bool GuidingQuemTheVirtuousSummonForSearch() + { + if (CheckWhetherNegated(true, true, CardType.Monster) || CheckWhetherWillbeRemoved()) return false; + if (activatedCardIdList.Contains(Card.Id)) return false; + if (Bot.HasInGraveyard(CardId.BrandedRetribution) && CheckRemainInDeck(CardId.BrandedFusion, CardId.BrandedLost, CardId.BrandedInWhite, CardId.BrandedInRed) > 0) + { + summoned = true; + return true; + } + if (Bot.HasInGraveyard(new[] { CardId.BrandedFusion, CardId.BrandedLost, CardId.BrandedBeast }) && CheckRemainInDeck(CardId.BrandedRetribution) > 0) + { + summoned = true; + return true; + } + return false; + } + + public bool GuidingQuemTheVirtuousActivate() + { + long desc = -1; + if (ActivateDescription >= Util.GetStringId(CardId.GuidingQuemTheVirtuous, 0)) + { + desc = ActivateDescription - Util.GetStringId(CardId.GuidingQuemTheVirtuous, 0); + } + Logger.DebugWriteLine("Guiding desc: " + desc.ToString()); + Logger.DebugWriteLine("Guiding timing: " + CurrentTiming.ToString()); + Logger.DebugWriteLine("Guiding flag: " + quemSummonFlag.ToString()); + + // spsummon + if ((ActivateDescription == -1 && quemSummonFlag == 0) || ActivateDescription == Util.GetStringId(CardId.GuidingQuemTheVirtuous, 1)) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + List>> checkList = new List>> + { + new KeyValuePair>(CardId.AlbionTheSanctifireDragon, (c) => c.IsCanRevive() && !activatedCardIdList.Contains(CardId.AlbionTheSanctifireDragon) ), + new KeyValuePair>(CardId.MirrorjadeTheIcebladeDragon, (c) => c.IsCanRevive() ), + new KeyValuePair>(CardId.FallenOfAlbaz, (c) => + { + bool albazCanFusionFlag = CheckAlbazFusion(null, out List materialList); + if (albazCanFusionFlag && !Util.ChainContainsCard(new[] {CardId.AlbionTheBrandedDragon, CardId.LubellionTheSearingDragon}) && !spSummoningAlbaz) + { + bool albazFlag = materialList.Count > 1; + if (materialList.Count > 0) + { + ClientCard material = materialList[0]; + albazFlag |= material.HasType(CardType.Ritual | CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link); + albazFlag |= material.IsFloodgate() || material.IsOneForXyz() || Util.GetWorstBotMonster().GetDefensePower() < material.Attack; + } + return albazFlag; + } + return false; + } ), + new KeyValuePair>(CardId.AlbionTheBrandedDragon, (c) => c.IsCanRevive() && Bot.HasInSpellZone(CardId.BrandedBeast) + && Bot.MonsterZone.Any(oc => oc != null && oc.IsFaceup() && oc.HasSetcode(SetcodeBystial)) ), + new KeyValuePair>(CardId.BlazingCartesiaTheVirtuous, (c) => Duel.Player == 0 || !activatedCardIdList.Contains(CardId.BlazingCartesiaTheVirtuous + 1) ), + new KeyValuePair>(CardId.TriBrigadeMercourier, (c) => Bot.MonsterZone.Any(oc => oc != null && oc.IsFaceup() && oc.IsCode(albazFusionMonster)) ), + new KeyValuePair>(CardId.AlbionTheSanctifireDragon, (c) => c.IsCanRevive() ), + new KeyValuePair>(CardId.SpringansKitt, (c) => true ) + }; + foreach (KeyValuePair> pair in checkList) + { + ClientCard target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsOriginalCode(pair.Key) && pair.Value(c)); + if (target != null) + { + if (target.IsOriginalCode(CardId.FallenOfAlbaz)) + { + spSummoningAlbaz = true; + } + AI.SelectCard(target); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + // send to GY + if ((ActivateDescription == -1 && quemSummonFlag > 0) || ActivateDescription == Util.GetStringId(CardId.GuidingQuemTheVirtuous, 0)) + { + if (CheckWhetherNegated(true, true, CardType.Monster) || CheckWhetherWillbeRemoved()) return false; + quemSummonFlag = 0; + activatedCardIdList.Add(Card.Id); + return true; + } + return false; + } + + public bool BlazingCartesiaTheVirtuousSummon() + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + bool checkFlag = Bot.HasInHandOrInSpellZone(CardId.BrandedOpening) && !activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && CheckRemainInDeck(CardId.AluberTheJesterOfDespia) > 0; + checkFlag |= Bot.HasInHand(CardId.AlbionTheShroudedDragon) && !activatedCardIdList.Contains(CardId.AlbionTheShroudedDragon); + checkFlag |= Bot.HasInHandOrHasInMonstersZone(CardId.BystialSaronir) && !activatedCardIdList.Contains(CardId.BystialSaronir + 1); + if (Bot.HasInExtra(CardId.GranguignolTheDuskDragon)) + { + bool hasMaterial = Bot.Hand.Any(c => c != Card && c.Attack < 2000 && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark)); + hasMaterial |= Bot.MonsterZone.Any(c => c != null && !c.IsCode(cannotBeFusionMaterialIdList) && c.Attack < 2000 && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark)); + checkFlag |= hasMaterial; + } + + if (checkFlag) + { + summoned = true; + return true; + } + + return false; + } + + public bool BlazingCartesiaTheVirtuousActivate() + { + // sp summon + if (Card.Location == CardLocation.Hand) + { + if (CheckShouldNoMoreSpSummon() || CheckWhetherNegated(true, true, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + // fusion + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + if (Duel.CurrentChain.Any(c => c != null && c.Controller == 0 && c.IsCode(CardId.BrandedInRed))) return false; + List materialList = Bot.MonsterZone.Where(c => c != null && c.Attack <= 2500 && !c.IsCode(cannotBeFusionMaterialIdList)).ToList(); + materialList.AddRange(Bot.Hand.Where(c => c.IsMonster() + && !(CheckWhetherCanSummon() && + ((!activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && c.IsCode(CardId.AluberTheJesterOfDespia)) + || (!activatedCardIdList.Contains(CardId.SpringansKitt) && c.IsCode(CardId.SpringansKitt))) + ) + ) + ); + + // escape target + ClientCard lastCahinCard = Util.GetLastChainCard(); + if (lastCahinCard != null && Duel.LastChainPlayer == 1) + { + List chainTargetList = Duel.LastChainTargets.Where(c => c.Controller == 0 && c.Location == CardLocation.MonsterZone + && (!c.IsCode(cannotBeFusionMaterialIdList) || c.Attack <= 2500)).ToList(); + if (chainTargetList.Count > 0) + { + if (lastCahinCard.IsCode(targetNegateIdList)) + { + chainTargetList = chainTargetList.Where(c => c.Attack <= 2500).ToList(); + } + BlazingCartesiaTheVirtuousFusionCheck(Bot.ExtraDeck, 0, materialList, chainTargetList, + out ClientCard _fusionTarget, out List usedMaterialList); + + if (_fusionTarget != null) + { + Logger.DebugWriteLine("cartesia prepare fusion1: " + _fusionTarget.Name); + cartesiaMaterialList.AddRange(usedMaterialList.Intersect(chainTargetList)); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + + // fusion shrouded + bool skipShroudedFlag = CheckWhetherCanSummon() && !activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && Bot.HasInHand(CardId.AluberTheJesterOfDespia); + skipShroudedFlag |= CheckWhetherCanSummon() && !activatedCardIdList.Contains(CardId.SpringansKitt) && Bot.HasInHand(CardId.SpringansKitt); + if (!skipShroudedFlag) + { + ClientCard shrouded = Duel.CurrentChain.FirstOrDefault(c => c.Controller == 0 && c.Location == CardLocation.Hand && c.IsOriginalCode(CardId.AlbionTheShroudedDragon)); + if (shrouded != null) + { + BlazingCartesiaTheVirtuousFusionCheck(Bot.ExtraDeck, 0, materialList, new List { shrouded }, + out ClientCard _fusionTarget, out List usedMaterialList); + + if (_fusionTarget != null) + { + Logger.DebugWriteLine("cartesia prepare fusion2: " + _fusionTarget.Name); + cartesiaMaterialList.AddRange(usedMaterialList.Intersect(new List { shrouded })); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + + bool shouldActivateFlag = Duel.Player == 0 && !CheckShouldNoMoreSpSummon() || Duel.Player == 1; + + // summon mirrorjade + bool checkMirrorJadeFlag = !(Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) || Bot.HasInSpellZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true)) + && shouldActivateFlag; + if (checkMirrorJadeFlag) + { + BlazingCartesiaTheVirtuousFusionCheck(Bot.ExtraDeck, CardId.MirrorjadeTheIcebladeDragon, materialList, null, + out ClientCard _fusionTarget, out _); + + if (_fusionTarget != null) + { + Logger.DebugWriteLine("cartesia prepare fusion3: " + _fusionTarget.Name); + cartesiaSummonGoal = CardId.MirrorjadeTheIcebladeDragon; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + + // summon dusk dragon + if (shouldActivateFlag && Duel.Player == 0) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.BystialSaronir, () => !activatedCardIdList.Contains(CardId.BystialSaronir + 1) && !DefaultCheckWhetherCardIdIsNegated(CardId.BystialSaronir) }, + {CardId.DespianTragedy, () => !activatedCardIdList.Contains(CardId.DespianTragedy) && !DefaultCheckWhetherCardIdIsNegated(CardId.DespianTragedy) } + }; + foreach (KeyValuePair> pair in checkDict) + { + ClientCard targetMaterial = materialList.FirstOrDefault(c => c.IsCode(pair.Key)); + if (targetMaterial != null && pair.Value()) + { + BlazingCartesiaTheVirtuousFusionCheck(Bot.ExtraDeck, CardId.GranguignolTheDuskDragon, materialList, new List { targetMaterial }, + out ClientCard _fusionTarget, out List usedMaterialList); + if (_fusionTarget != null) + { + Logger.DebugWriteLine("cartesia prepare fusion4: " + _fusionTarget.Name); + cartesiaSummonGoal = CardId.GranguignolTheDuskDragon; + cartesiaMaterialList.Add(targetMaterial); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + } + + // summon sanctifire + if (shouldActivateFlag) + { + BlazingCartesiaTheVirtuousFusionCheck(Bot.ExtraDeck, CardId.AlbionTheSanctifireDragon, materialList, new List { Card}, + out ClientCard _fusionTarget, out List usedMaterialList); + if (_fusionTarget != null) + { + Logger.DebugWriteLine("cartesia prepare fusion5: " + _fusionTarget.Name); + cartesiaSummonGoal = CardId.AlbionTheSanctifireDragon; + cartesiaMaterialList.Add(Card); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + + if (shouldActivateFlag && GetProblematicEnemyMonster(0, true, true, CardType.Monster) != null) + { + BlazingCartesiaTheVirtuousFusionCheck(Bot.ExtraDeck, CardId.BorreloadFuriousDragon, materialList, null, + out ClientCard _fusionTarget, out _); + + if (_fusionTarget != null) + { + Logger.DebugWriteLine("cartesia prepare fusion6: " + _fusionTarget.Name); + cartesiaSummonGoal = CardId.BorreloadFuriousDragon; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + + if (shouldActivateFlag) + { + bool checkFlag = Duel.Player == 0 && CurrentTiming == -1; + checkFlag |= Duel.Player == 1 && (CurrentTiming & hintTimingMainEnd) != 0; + if (checkFlag) + { + BlazingCartesiaTheVirtuousFusionCheck(Bot.ExtraDeck, 0, materialList, null, + out ClientCard _fusionTarget, out _); + + if (_fusionTarget != null) + { + Logger.DebugWriteLine("cartesia prepare fusion7: " + _fusionTarget.Name); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + } + return false; + } + + public bool BlazingCartesiaTheVirtuousActivateInGrave() + { + // recycle + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id + 2); + return true; + } + return false; + } + + public void BlazingCartesiaTheVirtuousFusionCheck( + IList canSummonList, int mustSummonId, + List materialList, List mustMaterialList, + out ClientCard fusionTarget, out List selectedFusionMaterialList) + { + fusionTarget = null; + selectedFusionMaterialList = new List(); + + Dictionary>> checkDict = new Dictionary>> + { + {CardId.GranguignolTheDuskDragon, new List>{ + (c) => c.IsCode(CardId.BlazingCartesiaTheVirtuous), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark) + } }, + {CardId.AlbionTheSanctifireDragon, new List>{ + (c) => c.IsCode(CardId.FallenOfAlbaz), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Light) && c.HasRace(CardRace.SpellCaster) + } }, + {CardId.MirrorjadeTheIcebladeDragon, new List> + { + (c) => c.IsCode(CardId.FallenOfAlbaz), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) + } }, + {CardId.AlbionTheBrandedDragon, new List> + { + (c) => c.IsCode(CardId.FallenOfAlbaz), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Light) + } }, + {CardId.LubellionTheSearingDragon, new List> + { + (c) => c.IsCode(CardId.FallenOfAlbaz), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Dark) + } }, + {CardId.DespianQuaeritis, new List> + { + (c) => c.HasSetcode(SetcodeDespain), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark) + } }, + {CardId.BorreloadFuriousDragon, new List> + { + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Dark) && c.HasRace(CardRace.Dragon), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Dark) && c.HasRace(CardRace.Dragon) + } } + }; + Dictionary> extraCheckDict = new Dictionary> + { + {CardId.AlbionTheSanctifireDragon, (c1, c2) => + { + int reviveCount = Bot.Graveyard.Count(c => c != null && c.IsMonster() && c.IsCanRevive()); + reviveCount += Enemy.Graveyard.Count(c => c != null && c.IsMonster() && c.IsCanRevive()); + if (!CheckWhetherWillbeRemoved() || + (CurrentTiming & hintTimingMainEnd) > 0 && Util.GetOneEnemyBetterThanValue(Card.GetDefensePower()) != null + && Util.GetOneEnemyBetterThanValue(3000) == null) + { + reviveCount += 2; + } + return reviveCount >= 2; + } }, + {CardId.LubellionTheSearingDragon, (c1, c2) => Bot.Hand.Count(c => c != c1 && c != c2) > 0 }, + {CardId.MirrorjadeTheIcebladeDragon, (c1, c2) => !CheckWhetherWillbeRemoved() && + !Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) && !Bot.HasInSpellZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) } + }; + + foreach (KeyValuePair>> pair in checkDict) + { + if (mustSummonId > 0 && mustSummonId != pair.Key) continue; + ClientCard currentFusionTarget = canSummonList.FirstOrDefault(c => c != null && c.IsCode(pair.Key)); + if (currentFusionTarget == null) continue; + Func fusionFunc1 = pair.Value[0]; + Func fusionFunc2 = pair.Value[1]; + + if (mustMaterialList != null && mustMaterialList.Count > 0) + { + foreach (ClientCard mustMaterial in mustMaterialList) + { + if (!fusionFunc1(mustMaterial) && !fusionFunc2(mustMaterial)) continue; + foreach (ClientCard anotherMaterial in materialList) + { + if (anotherMaterial == mustMaterial) continue; + bool checkFlag = fusionFunc1(mustMaterial) && fusionFunc2(anotherMaterial); + checkFlag |= fusionFunc2(mustMaterial) && fusionFunc1(anotherMaterial); + extraCheckDict.TryGetValue(pair.Key, out Func extraCheckFunc); + checkFlag &= (extraCheckFunc == null || extraCheckFunc(mustMaterial, anotherMaterial)); + if (checkFlag) + { + fusionTarget = currentFusionTarget; + selectedFusionMaterialList.Add(mustMaterial); + selectedFusionMaterialList.Add(anotherMaterial); + return; + } + } + } + } + else + { + for (int index1 = 0; index1 < materialList.Count - 1; ++index1) + { + ClientCard material1 = materialList[index1]; + if (!fusionFunc1(material1) && !fusionFunc2(material1)) continue; + for (int index2 = index1 + 1; index2 < materialList.Count; ++index2) + { + ClientCard material2 = materialList[index2]; + bool checkFlag = fusionFunc1(material1) && fusionFunc2(material2); + checkFlag |= fusionFunc2(material1) && fusionFunc1(material2); + extraCheckDict.TryGetValue(pair.Key, out Func extraCheckFunc); + checkFlag &= (extraCheckFunc == null || extraCheckFunc(material1, material2)); + if (checkFlag) + { + fusionTarget = currentFusionTarget; + selectedFusionMaterial.Add(material1); + selectedFusionMaterial.Add(material2); + return; + } + } + } + } + } + } + + public bool TriBrigadeMercourierActivate() + { + // negate + if (Card.Location == CardLocation.Hand || Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, false, CardType.Monster) || !CheckLastChainShouldNegated()) return false; + if (Util.GetLastChainCard().Location == CardLocation.MonsterZone) currentNegateCardList.Add(Util.GetLastChainCard()); + activatedCardIdList.Add(Card.Id); + return true; + } + // search + if (Card.Location == CardLocation.Removed) + { + return false; + } + return false; + } + + public bool TriBrigadeMercourierActivateForSearch() + { + // negate + if (Card.Location == CardLocation.Hand || Card.Location == CardLocation.MonsterZone) + { + return false; + } + // search + if (Card.Location == CardLocation.Removed) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + return false; + } + + public bool AshBlossomActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC)) return false; + if (DefaultAshBlossomAndJoyousSpring()) + { + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard.Location == CardLocation.MonsterZone || lastChainCard.Location == CardLocation.SpellZone) currentNegateCardList.Add(Util.GetLastChainCard()); + return true; + } + return false; + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); + } + + public bool DespianTragedyActivate() + { + // search + if (ActivateDescription != Util.GetStringId(Card.Id, 1)) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + // set + else { + if (CheckWhetherNegated(true, false, CardType.Trap)) return false; + Dictionary> checkDict = new Dictionary>{ + {CardId.BrandedFusion, () => BrandedFusionActivateCheck()}, + {CardId.BrandedLost, () => { + if (Duel.Player == 0 && Duel.Phase >= DuelPhase.End) return false; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedFusion) && BrandedFusionActivateCheck()) return true; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedInWhite) && BrandedInWhiteActivateCheck()) return true; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedInRed) && BrandedInRedActivateCheck() != null) return true; + if (!summoned && Bot.HasInHand(CardId.FallenOfAlbaz) && CheckAlbazFusion()) return true; + if ((Bot.HasInMonstersZone(CardId.BlazingCartesiaTheVirtuous) || (!summoned && Bot.HasInHand(CardId.BlazingCartesiaTheVirtuous)))) return true; + return false; + } }, + {CardId.BrandedInHighSpirits, BrandedInHighSpiritsActivateCheck}, + {CardId.BrandedInRed, () => BrandedInRedActivateCheck() != null }, + {CardId.BrandedInWhite, BrandedInWhiteActivateCheck }, + {CardId.BrightestBlazingBrandedKing, () => Bot.GetMonsters().Any(c => c.IsFaceup() && c.IsCode(albazFusionMonster)) }, + {CardId.BrandedOpening, () => Bot.Hand.Count > 2 && !activatedCardIdList.Contains(CardId.BrandedOpening) } + }; + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = Bot.Graveyard.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + activatedCardIdList.Add(Card.Id); + AI.SelectCard(target); + SelectSTPlace(target, true); + return true; + } + } + } + return false; + } + + public bool DespianTragedySet() + { + if (Bot.Graveyard.Any(c => c != null && c.HasType(CardType.Spell | CardType.Trap) && c.HasSetcode(SetcodeBranded))) + { + summoned = true; + return true; + } + + return false; + } + + public bool NadirServantActivate() + { + if (CheckWhetherNegated(true, true, CardType.Spell) || CheckWhetherWillbeRemoved()) return false; + bool checkResult = NadirServantActivateCheck(null, false, out _); + if (checkResult) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public bool NadirServantActivateCheck(IList cards, bool force, out ClientCard target) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.AlbionTheBrandedDragon, () => !sendToGYThisTurn.Any(c => c.IsCode(CardId.AlbionTheBrandedDragon)) }, + {CardId.DespianLuluwalilith, () => CheckRemainInDeck(CardId.BlazingCartesiaTheVirtuous, CardId.GuidingQuemTheVirtuous) > 0 }, + {CardId.TitanikladTheAshDragon, () => CheckRemainInDeck(CardId.GuidingQuemTheVirtuous) > 0 }, + {CardId.SprindTheIrondashDragon, () => CheckRemainInDeck(CardId.SpringansKitt) > 0 }, + {CardId.RindbrummTheStrikingDragon, () => Bot.Graveyard.Any(c => c != null && c.IsOriginalCode(CardId.FallenOfAlbaz)) }, + {CardId.AlbaLenatusTheAbyssDragon, () => force && CheckRemainInDeck(CardId.FusionDeployment, CardId.BrandedFusion) > 0 }, + {CardId.GranguignolTheDuskDragon, () => force}, + }; + + foreach (KeyValuePair> pair in checkDict) + { + if (cards == null) + { + if (Bot.HasInExtra(pair.Key) && pair.Value()) + { + target = null; + return true; + } + } else + { + ClientCard tg = cards.FirstOrDefault(c => c.IsOriginalCode(pair.Key)); + if (tg != null && pair.Value()) + { + target = tg; + return true; + } + } + } + + target = null; + return false; + } + + public bool FusionDeploymentActivate() + { + if (CheckWhetherNegated(true, true, CardType.Spell)) return false; + if (FusionDeploymentSpSummonTarget() > 0 && !Bot.HasInHand(CardId.BrandedLost)) + { + SelectSTPlace(Card, true); + return true; + } + + return false; + } + + public int FusionDeploymentSpSummonTarget() + { + if (CheckRemainInDeck(CardId.FallenOfAlbaz) > 0 && CheckAlbazFusion(Card) && GetProblematicEnemyMonster(0, false, false, CardType.Monster) != null) + { + return CardId.FallenOfAlbaz; + } + if (CheckRemainInDeck(CardId.BlazingCartesiaTheVirtuous) > 0 && Bot.HasInExtra(CardId.GranguignolTheDuskDragon)) + { + if (Bot.Hand.Any(c => c.IsMonster() && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark)) + || Bot.GetMonsters().Any(c => c.IsMonster() && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark) && !c.IsCode(cannotBeFusionMaterialIdList))) + { + return CardId.BlazingCartesiaTheVirtuous; + } + } + if (CheckRemainInDeck(CardId.FallenOfAlbaz) > 0 && CheckAlbazFusion(Card)) + { + return CardId.FallenOfAlbaz; + } + return 0; + } + + public bool BrandedInWhiteActivate() + { + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Spell)) return false; + activatedCardIdList.Add(Card.Id + 1); + SelectSTPlace(Card); + return true; + } else + { + if (_BrandedInWhiteActivateCheck(true)) + { + activatedCardIdList.Add(Card.Id); + SelectSTPlace(Card, true); + return true; + } + } + return false; + } + + public bool BrandedInWhiteActivateCheck() + { + return _BrandedInWhiteActivateCheck(false); + } + + public bool _BrandedInWhiteActivateCheck(bool activate = false) + { + if (CheckWhetherNegated(true, true, CardType.Spell) || activatedCardIdList.Contains(CardId.BrandedInWhite) || nadirActivated) return false; + if (CheckShouldNoMoreSpSummon() && Bot.MonsterZone.Any(c => c != null && c.GetDefensePower() >= 2000)) return false; + if (BrandedInWhiteFusionTarget(Bot.ExtraDeck, out ClientCard _fusionTarget) > 0) + { + if (activate) Logger.DebugWriteLine("White prepare fusion: " + _fusionTarget?.Name); + return true; + } + + return false; + } + + public int BrandedInWhiteFusionTarget(IList cards, out ClientCard target) + { + target = null; + Dictionary> checkDict = new Dictionary> + { + {CardId.MirrorjadeTheIcebladeDragon, () => { + if (Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) || Bot.HasInSpellZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true)) return false; + bool albazFlag = Bot.Graveyard.Any(c => c.IsCode(CardId.FallenOfAlbaz)); + albazFlag |= Bot.MonsterZone.Any(c => c != null && c.IsOriginalCode(CardId.FallenOfAlbaz)); + albazFlag |= Bot.Hand.Any(c => c.IsOriginalCode(CardId.FallenOfAlbaz)); + if (!albazFlag) return false; + + bool checkFlag = Bot.Graveyard.Any(c => c != null && !sendToGYThisTurn.Contains(c) && !c.IsCode(cannotBeFusionMaterialIdList) + && c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)); + checkFlag |= Bot.MonsterZone.Any(c => c != null && !c.IsCode(cannotBeFusionMaterialIdList) + && (c.IsCode(albazFusionMonster) || c.IsCode(CardId.GranguignolTheDuskDragon))); + return checkFlag; + } }, + {CardId.BorreloadFuriousDragon, () => { + if (Enemy.GetMonsterCount() + Enemy.GetSpellCount() > 0) + { + List darkDragonList = Bot.Hand.Where(c => c != null && c.IsMonster() && c.HasAttribute(CardAttribute.Dark) && c.HasRace(CardRace.Dragon)).ToList(); + darkDragonList.AddRange(Bot.MonsterZone.Where(c => c != null && c.IsMonster() && c.HasAttribute(CardAttribute.Dark) && c.HasRace(CardRace.Dragon) && !c.IsCode(cannotBeFusionMaterialIdList)).ToList()); + List graveDarkDragonList = Bot.Graveyard.Where(c => c.HasRace(CardRace.Dragon) && c.HasAttribute(CardAttribute.Dark) + && !c.IsCode(cannotBeFusionMaterialIdList) && !CheckWhetherShouldKeepInGrave(c) ).ToList(); + + bool hasAlbaz = darkDragonList.Any(c => c.IsCode(CardId.FallenOfAlbaz)) || graveDarkDragonList.Any(c => c.IsCode(CardId.FallenOfAlbaz)); + int darkDragonCount = darkDragonList.Count; + if (hasAlbaz) + { + darkDragonCount += graveDarkDragonList.Count; + } + return darkDragonCount >= 2; + } + return false; + } }, + {CardId.GuardianChimera, () => + { + if (CheckWhetherNegated(true, true, CardType.Monster) || DefaultCheckWhetherCardIdIsNegated(CardId.GuardianChimera)) return false; + int enemyCardCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); + if (enemyCardCount == 0) return false; + + return ChimeraFusionMaterialList().Count > 0; + } }, + {CardId.LubellionTheSearingDragon, () => + { + if (activatedCardIdList.Contains(CardId.LubellionTheSearingDragon) + || DefaultCheckWhetherCardIdIsNegated(CardId.LubellionTheSearingDragon) + || CheckWhetherNegated(true, true, CardType.Monster)) + { + return false; + } + List checkMaterialList = new List(Bot.Graveyard.Where(c => c != null && c.IsMonster()).OrderBy(c => c.GetDefensePower())).ToList(); + checkMaterialList.AddRange(Bot.GetMonsters().OrderBy(c => c.GetDefensePower())); + checkMaterialList.AddRange(Bot.Hand); + ClientCard albaz = checkMaterialList.Where(c => c.IsCode(CardId.FallenOfAlbaz)).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + ClientCard darkMonster = checkMaterialList.Where(c => c != albaz && c.HasAttribute(CardAttribute.Dark)).FirstOrDefault(); + if (albaz == null || darkMonster == null) return false; + if (Bot.Hand.Count(c => c != albaz && c != darkMonster && !c.IsCode(CardId.BrandedInWhite)) == 0) return false; + + return true; + } }, + {CardId.AlbionTheSanctifireDragon, () => + { + List checkMaterialList = new List(Bot.Graveyard.Where(c => c != null && c.IsMonster()).OrderBy(c => c.GetDefensePower())).ToList(); + checkMaterialList.AddRange(Bot.GetMonsters().OrderBy(c => c.GetDefensePower())); + checkMaterialList.AddRange(Bot.Hand); + ClientCard albaz = checkMaterialList.FirstOrDefault(c => c.IsCode(CardId.FallenOfAlbaz)); + ClientCard lightSpellcaster = checkMaterialList.FirstOrDefault(c => c.HasRace(CardRace.SpellCaster) && c.HasAttribute(CardAttribute.Light)); + if (albaz == null || lightSpellcaster == null) return false; + + int remainMonsterCount = Enemy.GetGraveyardMonsters().Count; + remainMonsterCount += Bot.Graveyard.Where(c => c.IsMonster() && c != albaz && c != lightSpellcaster).Count(); + remainMonsterCount += Bot.HasInHand(_CardId.MaxxC) ? 1 : 0; + return remainMonsterCount >= 2; + } }, + {CardId.RindbrummTheStrikingDragon, () => { + if (!Bot.HasInGraveyard(CardId.TriBrigadeMercourier)) return false; + bool albazCheck = Bot.HasInHandOrHasInMonstersZone(CardId.FallenOfAlbaz); + albazCheck |= Bot.HasInGraveyard(CardId.FallenOfAlbaz); + return albazCheck; + } }, + {CardId.DespianQuaeritis, () => + { + bool checkFlag = Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && c.Attack >= 2500 && !(c.HasType(CardType.Fusion) && c.Level >= 8)); + if (checkFlag) + { + ClientCard despianInGrave = Bot.Graveyard.Where(c => c != null && c.HasSetcode(SetcodeDespain) && !CheckWhetherShouldKeepInGrave(c)) + .OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (despianInGrave != null) + { + bool albazCheck = Bot.HasInHandOrHasInMonstersZone(CardId.FallenOfAlbaz); + albazCheck |= Bot.HasInGraveyard(CardId.FallenOfAlbaz); + return albazCheck; + } + List fusionMaterialList = Bot.Hand.Where(c => c.IsMonster()).OrderBy(c => c.GetDefensePower()).ToList(); + fusionMaterialList.AddRange(Bot.MonsterZone.Where(c => c != null && !c.IsCode(cannotBeFusionMaterialIdList)).OrderBy(c => c.GetDefensePower()).ToList()); + ClientCard despian = fusionMaterialList.FirstOrDefault(c => c.HasSetcode(SetcodeDespain)); + if (despian != null) + { + checkFlag = fusionMaterialList.Any(c => c != despian && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark)); + checkFlag |= Bot.HasInGraveyard(CardId.FallenOfAlbaz); + return checkFlag; + } + } + + return false; + } }, + {CardId.TitanikladTheAshDragon, () => + { + List checkMaterialList = new List(Bot.Graveyard.Where(c => c != null && c.IsMonster()).OrderBy(c => c.GetDefensePower())).ToList(); + checkMaterialList.AddRange(Bot.GetMonsters().OrderBy(c => c.GetDefensePower())); + checkMaterialList.AddRange(Bot.Hand); + ClientCard albaz = checkMaterialList.Where(c => c.IsCode(CardId.FallenOfAlbaz)).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + + foreach (ClientCard material in checkMaterialList) + { + if (material != albaz && material.IsMonster() && material.Attack >= 2500 && !material.IsCode(cannotBeFusionMaterialIdList) && !Util.IsTurn1OrMain2()) + { + bool checkFlag = Enemy.GetMonsterCount() == 0 && !CheckWhetherShouldKeepInGrave(material) && (material.IsFacedown() || material.Location != CardLocation.MonsterZone); + + int expectedAttack = 2900 + material.Level * 100; + int botBestPower = Util.GetBestPower(Bot); + int beforeBetterCount = Enemy.MonsterZone.Count(c => c != null && c.GetDefensePower() >= botBestPower); + int afterBetterCount = Enemy.MonsterZone.Count(c => c != null && c.GetDefensePower() >= expectedAttack); + checkFlag |= afterBetterCount < beforeBetterCount; + + return checkFlag; + } + } + return false; + } }, + {CardId.AlbaLenatusTheAbyssDragon, () => + { + if (Util.GetOneEnemyBetterThanMyBest() == null && Duel.MainPhase.CanBattlePhase) + { + ClientCard albaz = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsOriginalCode(CardId.FallenOfAlbaz)); + if (albaz == null) + { + albaz = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsCode(CardId.FallenOfAlbaz)); + } + if (albaz == null) return false; + foreach (ClientCard material in Bot.Graveyard) + { + if (material != null && material != albaz && material.IsMonster() && material.HasRace(CardRace.Dragon) && !material.IsCode(cannotBeFusionMaterialIdList)) + { + return true; + } + } + } + return false; + } } + }; + + foreach (KeyValuePair> pair in checkDict) + { + target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return pair.Key; + } + } + + target = null; + return 0; + } + + public List ChimeraFusionMaterialList(bool dragonCheck = true) + { + int enemyCardCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); + + List fieldMonsterList = Bot.MonsterZone.Where(c => c != null && c.GetDefensePower() <= 2500 && !c.IsCode(cannotBeFusionMaterialIdList)) + .OrderBy(c => c.GetDefensePower()).ToList(); + List handMonsterList = Bot.Hand.Where(c => c.IsMonster()) + .OrderBy(c => c.GetDefensePower()).ToList(); + // 2 monsters on field + 1 monster in hand + if (enemyCardCount >= 2 && fieldMonsterList.Count >= 2) + { + if (fieldMonsterList.Count < 2 || handMonsterList.Count < 1) return new List() ; + foreach (ClientCard handMonster in handMonsterList) + { + for (int fieldIndex1 = 0; fieldIndex1 < fieldMonsterList.Count - 1; ++fieldIndex1) + { + ClientCard fieldMonster1 = fieldMonsterList[fieldIndex1]; + if (fieldMonster1.IsCode(handMonster.Id) || handMonster.IsCode(fieldMonster1.Id)) continue; + for (int fieldIndex2 = fieldIndex1 + 1; fieldIndex2 < fieldMonsterList.Count; ++fieldIndex2) + { + ClientCard fieldMonster2 = fieldMonsterList[fieldIndex2]; + if (fieldMonster2.IsCode(handMonster.Id) || handMonster.IsCode(fieldMonster2.Id)) continue; + if (fieldMonster2.IsCode(fieldMonster1.Id) || fieldMonster1.IsCode(fieldMonster2.Id)) continue; + + List materialList = new List { handMonster, fieldMonster1, fieldMonster2 }; + bool checkFlag = dragonCheck && materialList.Any(c => c.HasRace(CardRace.Dragon)); + if (checkFlag) + { + return materialList; + } + } + } + } + } + // 1 monster on field + 2 monsters in hand + if (enemyCardCount == 1 || fieldMonsterList.Count == 1) + { + if (fieldMonsterList.Count < 1 || handMonsterList.Count < 2) return new List(); + foreach (ClientCard fieldMonster in fieldMonsterList) + { + for (int handIndex1 = 0; handIndex1 < handMonsterList.Count - 1; ++handIndex1) + { + ClientCard handMonster1 = handMonsterList[handIndex1]; + if (handMonster1.IsCode(fieldMonster.Id) || fieldMonster.IsCode(handMonster1.Id)) continue; + for (int handIndex2 = handIndex1 + 1; handIndex2 < handMonsterList.Count; ++handIndex2) + { + ClientCard handMonster2 = handMonsterList[handIndex2]; + if (handMonster2.IsCode(fieldMonster.Id) || fieldMonster.IsCode(handMonster2.Id)) continue; + if (handMonster2.IsCode(handMonster1.Id) || handMonster1.IsCode(handMonster2.Id)) continue; + + List materialList = new List { fieldMonster, handMonster1, handMonster2 }; + bool checkFlag = dragonCheck && materialList.Any(c => c.HasRace(CardRace.Dragon)); + if (checkFlag) + { + return materialList; + } + } + } + } + } + + return new List(); + } + + public bool BrandedFusionActivate() + { + if (BrandedFusionActivateCheck()) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public bool BrandedFusionActivateCheck(bool endPhaseCheck = true) + { + if (CheckWhetherNegated(true, true, CardType.Spell) || activatedCardIdList.Contains(CardId.BrandedFusion)) return false; + if (!Bot.HasInHandOrHasInMonstersZone(CardId.FallenOfAlbaz) && CheckRemainInDeck(CardId.FallenOfAlbaz) == 0) return false; + if (endPhaseCheck && Duel.Phase >= DuelPhase.End) return false; + return true; + } + + public bool GoldSarcophagusActivate() + { + if (CheckWhetherNegated(true, true, CardType.Spell)) return false; + if (GoldSarcophagusTarget(null, out _) > 0) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public int GoldSarcophagusTarget(IList cards, out ClientCard target) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.DespianTragedy, () => !activatedCardIdList.Contains(CardId.DespianTragedy) && !DefaultCheckWhetherCardIdIsNegated(CardId.DespianTragedy) }, + {CardId.TriBrigadeMercourier, () => !activatedCardIdList.Contains(CardId.TriBrigadeMercourier + 1) && !DefaultCheckWhetherCardIdIsNegated(CardId.TriBrigadeMercourier) } + }; + foreach (KeyValuePair> pair in checkDict) + { + int cardId = pair.Key; + if (pair.Value()) + { + if (cards != null) + { + target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return cardId; + } + } + else if (CheckRemainInDeck(cardId) > 0) + { + target = null; + return cardId; + } + } + } + + target = null; + return 0; + } + + public bool FoolishBurialActivate() + { + if (CheckWhetherNegated(true, true, CardType.Spell) || CheckWhetherWillbeRemoved()) return false; + if (FoolishBurialTarget(null, out _) > 0) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public int FoolishBurialTarget(IList cards, out ClientCard target) + { + // tragedy + if (!activatedCardIdList.Contains(CardId.DespianTragedy) && !DefaultCheckWhetherCardIdIsNegated(CardId.DespianTragedy)) + { + if (cards != null) + { + target = cards.FirstOrDefault(c => c.IsCode(CardId.DespianTragedy)); + if (target != null) + { + return CardId.DespianTragedy; + } + } else + { + if (CheckRemainInDeck(CardId.DespianTragedy) > 0) + { + target = null; + return CardId.DespianTragedy; + } + } + } + + // send to GY check + bool sendToGYFlag = CheckRemainInDeck(CardId.BrandedRetribution) > 0; + sendToGYFlag |= Bot.HasInGraveyard(CardId.BrandedRetribution) && CheckRemainInDeck(CardId.BrandedFusion) > 0; + if (sendToGYFlag) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.BystialSaronir, () => !activatedCardIdList.Contains(CardId.BystialSaronir + 1) && !DefaultCheckWhetherCardIdIsNegated(CardId.BystialSaronir) }, + {CardId.AlbionTheShroudedDragon, () => !activatedCardIdList.Contains(CardId.AlbionTheShroudedDragon) && !DefaultCheckWhetherCardIdIsNegated(CardId.AlbionTheShroudedDragon) } + }; + + foreach (KeyValuePair> pair in checkDict) + { + int cardId = pair.Key; + if (pair.Value()) + { + if (cards != null) + { + target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return cardId; + } + } else if (CheckRemainInDeck(cardId) > 0) + { + target = null; + return cardId; + } + } + } + } + + // albaz + if (!Bot.HasInGraveyard(CardId.FallenOfAlbaz)) + { + bool albazCheckFlag = Bot.HasInHand(new List { CardId.BrandedInRed, CardId.BrandedInWhite }); + albazCheckFlag |= Bot.HasInHand(CardId.BlazingCartesiaTheVirtuous) && Bot.MonsterZone.Count(c => c != null && c.Sequence < 5) < 5; + if (albazCheckFlag) + { + int albazCountCheck = Bot.HasInHandOrInSpellZone(CardId.BrandedFusion) ? 2 : 1; + if (cards != null) + { + List albazList = cards.Where(c => c.IsCode(CardId.FallenOfAlbaz)).ToList(); + if (albazList.Count >= albazCountCheck) + { + target = albazList.First(); + return CardId.FallenOfAlbaz; + } + } else + { + if (CheckRemainInDeck(CardId.FallenOfAlbaz) >= albazCountCheck) + { + target = null; + return CardId.FallenOfAlbaz; + } + } + } + } + + target = null; + return 0; + } + + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (CheckAtAdvantage() && Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + return false; + } + if (Duel.LastChainPlayer == 1) + { + // negate + if (Util.GetLastChainCard().IsMonster()) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC) && CheckAtAdvantage()) + { + return false; + } + ClientCard graveTarget = Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.GetOriginCode() == code); + if (graveTarget != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(graveTarget); + currentDestroyCardList.Add(graveTarget); + return true; + } + } + + // banish target + foreach (ClientCard graveCard in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(graveCard) && graveCard.IsMonster()) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + int code = graveCard.Id; + AI.SelectCard(graveCard); + currentDestroyCardList.Add(graveCard); + return true; + } + } + + // become targets + if (Duel.ChainTargets.Contains(Card)) + { + List enemyMonsters = Enemy.Graveyard.GetMatchingCards(card => card.IsMonster()).ToList(); + if (enemyMonsters.Count > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + int code = enemyMonsters[0].Id; + AI.SelectCard(code); + currentDestroyCardList.Add(enemyMonsters[0]); + return true; + } + } + } + + // avoid danger monster in grave + if (Duel.LastChainPlayer == 1) return false; + List targets = GetDangerousCardinEnemyGrave(true); + if (targets.Count > 0) + { + int code = targets[0].Id; + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + currentDestroyCardList.Add(targets[0]); + return true; + } + + return false; + } + + public bool BrandedInHighSpiritsActivate() + { + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Spell)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } else + { + if (BrandedInHighSpiritsActivateCheck()) + { + activatedCardIdList.Add(Card.Id); + SelectSTPlace(Card, true); + return true; + } + } + return false; + } + + public bool BrandedInHighSpiritsActivateCheck() + { + bool lubellionCheck = Bot.HasInHand(CardId.TheBystialLubellion) && CheckRemainInDeck(CardId.BystialSaronir) > 0 && !activatedCardIdList.Contains(CardId.TheBystialLubellion) + && Duel.Player == 0 && (Duel.Phase <= DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) && !CheckWhetherWillbeRemoved(); + + if (CheckWhetherNegated(true, true, CardType.Spell) || activatedCardIdList.Contains(CardId.BrandedInHighSpirits) || CheckWhetherWillbeRemoved()) return false; + Dictionary> checkDict = new Dictionary> + { + {CardId.AlbionTheBrandedDragon, () => !sendToGYThisTurn.Any(c => c.IsCode(CardId.AlbionTheBrandedDragon)) && !lubellionCheck + && Bot.Hand.Any(c => BrandedInHighSpiritDiscardDragonCheck(c) ) }, + {CardId.TitanikladTheAshDragon, () => !sendToGYThisTurn.Any(c => c.IsCode(CardId.TitanikladTheAshDragon)) && !lubellionCheck + && Bot.Hand.Any(c => BrandedInHighSpiritDiscardDragonCheck(c) ) + && CheckRemainInDeck(CardId.GuidingQuemTheVirtuous, CardId.FallenOfAlbaz) > 0}, + {CardId.GranguignolTheDuskDragon, () => Bot.Hand.Any(c => c.HasRace(CardRace.SpellCaster) && !(CheckWhetherCanSummon() && c.IsOriginalCode(CardId.GuidingQuemTheVirtuous))) }, + {CardId.RindbrummTheStrikingDragon, () => Bot.Hand.Any(c => c.HasRace(CardRace.WindBeast) + && (!c.IsCode(CardId.TriBrigadeMercourier) || !Bot.MonsterZone.Any(c2 => c2 != null && c2.IsFaceup() && c2.IsCode(albazFusionMonster)))) } + }; + + foreach (KeyValuePair> pair in checkDict) + { + if (Bot.HasInExtra(pair.Key) && pair.Value()) + { + return true; + } + } + return false; + } + + public bool BrandedInHighSpiritDiscardDragonCheck(ClientCard card) + { + if (!card.HasRace(CardRace.Dragon)) return false; + if (Duel.Player == 0 && (Duel.Phase <= DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2)) + { + if (card.IsOriginalCode(CardId.AlbionTheShroudedDragon) && !activatedCardIdList.Contains(CardId.AlbionTheShroudedDragon)) return false; + if (card.IsOriginalCode(CardId.TheBystialLubellion) && !activatedCardIdList.Contains(CardId.TheBystialLubellion) && CheckRemainInDeck(CardId.BystialSaronir) > 0) return false; + } + + return true; + } + + public bool BrandedOpeningActivate() + { + if (CheckWhetherNegated(true, true, CardType.Spell)) return false; + if (Duel.Player == 0) + { + if (Bot.HasInHand(CardId.AlbionTheShroudedDragon) && !CheckWhetherWillbeRemoved() && !activatedCardIdList.Contains(CardId.AlbionTheShroudedDragon)) return false; + bool canCallCartesia = Bot.HasInHand(CardId.BlazingCartesiaTheVirtuous) && !summoned; + canCallCartesia |= !activatedCardIdList.Contains(CardId.FusionDeployment) && Bot.HasInHandOrInSpellZone(CardId.FusionDeployment) + && !CheckShouldNoMoreSpSummon() && Bot.HasInExtra(CardId.GranguignolTheDuskDragon) && CheckRemainInDeck(CardId.BlazingCartesiaTheVirtuous) > 0; + if (canCallCartesia) return false; + } + bool goal = CheckRemainInDeck(CardId.AluberTheJesterOfDespia) > 0 && !activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && !enemyActivateLockBird; + goal |= CheckRemainInDeck(CardId.GuidingQuemTheVirtuous) > 0; + if (goal) + { + SelectSTPlace(Card, true); + activatedCardIdList.Add(Card.Id); + return true; + } + return false; + } + + public bool CrossoutDesignatorActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + // negate + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard() != null) + { + int code = Util.GetLastChainCard().Id; + int alias = Util.GetLastChainCard().Alias; + if (alias != 0 && alias - code < 10) code = alias; + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (CheckRemainInDeck(code) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectAnnounceID(code); + return true; + } + } + return false; + } + + public bool BrandedInRedActivate() + { + ClientCard targetCard = BrandedInRedActivateCheck(true); + if (targetCard != null) + { + AI.SelectCard(targetCard); + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public ClientCard BrandedInRedActivateCheck(bool updateMaterialList = false) + { + if (CheckWhetherNegated(true, true, CardType.Spell) || activatedCardIdList.Contains(CardId.BrandedInRed)) return null; + if (Duel.CurrentChain.Any(c => c != null && c.Controller == 0 && c.IsCode(CardId.BlazingCartesiaTheVirtuous))) return null; + if (Duel.CurrentChain.Any(c => c != null && c.Controller == 0 && c.IsCode(CardId.AlbionTheBrandedDragon)) + && !Util.ChainContainPlayer(1)) return null; + if (nadirActivated) return null; + + List materialList = Bot.MonsterZone.Where(c => c != null && c.Attack <= 2500 && !c.IsCode(cannotBeFusionMaterialIdList)).ToList(); + materialList.AddRange(Bot.Hand.Where(c => c.IsMonster() + && !(CheckWhetherCanSummon() && + ((!activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) && c.IsCode(CardId.AluberTheJesterOfDespia)) + || (!activatedCardIdList.Contains(CardId.SpringansKitt) && c.IsCode(CardId.SpringansKitt))) + ) + ) + ); + + List graveTargetList = Bot.Graveyard.Where(c => c != null && c.IsMonster() && !c.HasType(CardType.Fusion | CardType.Synchro) + && (c.IsCode(CardId.FallenOfAlbaz) || c.HasSetcode(SetcodeDespain))).ToList(); + + // select targeted monster + if (Duel.LastChainPlayer == 1) + { + // escape grave monster + List targetedList = Duel.LastChainTargets.Where(c => c != null && c.Location == CardLocation.Grave && c.Controller == 0 + && !c.HasType(CardType.Fusion | CardType.Synchro) && (c.IsCode(CardId.FallenOfAlbaz) || c.HasSetcode(SetcodeDespain))).ToList(); + + if (targetedList.Count > 0) + { + foreach (ClientCard target in targetedList) + { + List newMaterialList = new List(materialList) { target }; + BrandedInRedFusionCheck(Bot.ExtraDeck, 0, newMaterialList, new List { target }, out ClientCard _fusionTarget, out _); + if (_fusionTarget != null) + { + if (updateMaterialList) Logger.DebugWriteLine("Red prepare fusion 1: " + _fusionTarget.Name); + return target; + } + } + } + + // escape target + ClientCard lastCahinCard = Util.GetLastChainCard(); + if (lastCahinCard != null) + { + List chainTargetList = Duel.LastChainTargets.Where(c => c.Controller == 0 && c.Location == CardLocation.MonsterZone + && (!c.IsCode(cannotBeFusionMaterialIdList) || c.Attack <= 2500)).ToList(); + if (chainTargetList.Count > 0) + { + if (lastCahinCard.IsCode(targetNegateIdList)) + { + chainTargetList = chainTargetList.Where(c => c.Attack <= 2500 && !c.IsCode(CardId.AlbionTheBrandedDragon)).ToList(); + } + + foreach (ClientCard target in graveTargetList) + { + List newMaterialList = new List(materialList) { target }; + BrandedInRedFusionCheck(Bot.ExtraDeck, 0, newMaterialList, chainTargetList, + out ClientCard _fusionTarget, out List usedMaterialList); + + if (_fusionTarget != null) + { + if (updateMaterialList) + { + Logger.DebugWriteLine("Red prepare fusion 2: " + _fusionTarget.Name); + brandedInRedMaterialList.AddRange(usedMaterialList.Intersect(chainTargetList)); + } + return target; + } + } + } + } + } + + bool shouldSummonFirst = Duel.Player == 0 && (Duel.Phase <= DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) && !summoned + && (Bot.HasInHand(new int[] { CardId.AluberTheJesterOfDespia, CardId.GuidingQuemTheVirtuous, CardId.SpringansKitt }) + || Bot.HasInHand(CardId.FallenOfAlbaz) && CheckAlbazFusion(Card)); + bool idleFlag = Duel.Player == 1 || CurrentTiming == -1; + + if (shouldSummonFirst || !idleFlag) return null; + + // for fusion searing dragon + if (!Bot.MonsterZone.Any(c => c != null && c.HasType(CardType.Fusion)) && Duel.LastChainPlayer != 0 + && !CheckWhetherNegated(true, true, CardType.Monster) && !activatedCardIdList.Contains(CardId.LubellionTheSearingDragon)) + { + foreach (ClientCard target in graveTargetList) + { + List newMaterialList = new List(materialList) { target }; + BrandedInRedFusionCheck(Bot.ExtraDeck, CardId.LubellionTheSearingDragon, newMaterialList, new List { target }, out ClientCard _fusionTarget, out _); + if (_fusionTarget != null) + { + return target; + } + } + } + + // remove cards + List problemCardList = GetProblematicEnemyCardList(false, false, CardType.Monster); + if (problemCardList.Count > 0 || (Duel.Phase == DuelPhase.End && Duel.Player == 1)) + { + if (!enemyActivateLockBird) + { + foreach (ClientCard target in graveTargetList) + { + List newMaterialList = new List(materialList) { target }; + BrandedInRedFusionCheck(Bot.ExtraDeck, CardId.GuardianChimera, newMaterialList, new List { target }, out ClientCard _fusionTarget, out _); + if (_fusionTarget != null) + { + if (updateMaterialList) Logger.DebugWriteLine("Red prepare fusion 3: " + _fusionTarget.Name); + return target; + } + } + } + + foreach (ClientCard target in graveTargetList) + { + List newMaterialList = new List(materialList) { target }; + BrandedInRedFusionCheck(Bot.ExtraDeck, CardId.BorreloadFuriousDragon, newMaterialList, new List { target }, out ClientCard _fusionTarget, out _); + if (_fusionTarget != null) + { + if (updateMaterialList) Logger.DebugWriteLine("Red prepare fusion 4: " + _fusionTarget.Name); + return target; + } + } + } + + return null; + } + + public void BrandedInRedFusionCheck( + IList canSummonList, int mustSummonId, + List materialList, List mustMaterialList, + out ClientCard fusionTarget, out List selectedFusionMaterialList) + { + fusionTarget = null; + selectedFusionMaterialList = new List(); + + // fusion id + // material condition + // extra check function + // search material function + List>, + Func, bool>, + Func, List, List>, Func, bool>, List> + >> checkTupleList = new List>, Func, bool>, + Func, List, List>, Func, bool>, List> + >> + { + new Tuple>, Func, bool>, + Func, List, List>, Func, bool>, List>>( + CardId.MirrorjadeTheIcebladeDragon, new List> + { + (c) => c.IsCode(CardId.FallenOfAlbaz), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) + }, + (l) => !Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) && !Bot.HasInSpellZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true), + BrandedInRedUsing2SubFunc + ), + new Tuple>, Func, bool>, + Func, List, List>, Func, bool>, List>>( + CardId.LubellionTheSearingDragon, new List> + { + (c) => c.IsCode(CardId.FallenOfAlbaz), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Dark) + }, + (l) => !Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) && !Bot.HasInSpellZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) + && Bot.Hand.Count(c => !l.Contains(c)) > 0 && !activatedCardIdList.Contains(CardId.LubellionTheSearingDragon), + BrandedInRedUsing2SubFunc + ), + new Tuple>, Func, bool>, + Func, List, List>, Func, bool>, List>>( + CardId.GuardianChimera, + null, + null, + BrandedInRedForChimeraFunc + ), + new Tuple>, Func, bool>, + Func, List, List>, Func, bool>, List>>( + CardId.DespianQuaeritis, new List> + { + (c) => c.HasSetcode(SetcodeDespain), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Light | CardAttribute.Dark) + }, + null, + BrandedInRedUsing2SubFunc + ), + new Tuple>, Func, bool>, + Func, List, List>, Func, bool>, List>>( + CardId.BorreloadFuriousDragon, new List> + { + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Dark) && c.HasRace(CardRace.Dragon), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Dark) && c.HasRace(CardRace.Dragon) + }, + null, + BrandedInRedUsing2SubFunc + ), + new Tuple>, Func, bool>, + Func, List, List>, Func, bool>, List>>( + CardId.AlbionTheSanctifireDragon, new List> + { + (c) => c.IsCode(CardId.FallenOfAlbaz), + (c) => !c.IsCode(cannotBeFusionMaterialIdList) && c.HasAttribute(CardAttribute.Light) && c.HasRace(CardRace.SpellCaster) + }, + (l) => Enemy.Graveyard.Count(c => c != null && c.IsMonster() && c.IsCanRevive()) + + Bot.Graveyard.Count(c => c != null && !l.Contains(c)) >= 2, + BrandedInRedUsing2SubFunc + ), + }; + + foreach (Tuple>, Func, bool>, + Func, List, List>, Func, bool>, List>> tuple in checkTupleList) + { + if (mustSummonId > 0 && mustSummonId != tuple.Item1) continue; + ClientCard currentFusionTarget = canSummonList.FirstOrDefault(c => c != null && c.IsCode(tuple.Item1)); + if (currentFusionTarget == null) continue; + Func, List, List>, Func, bool>, List> materialFunc = tuple.Item4; + + List currentMaterialList = materialFunc(materialList, mustMaterialList, tuple.Item2, tuple.Item3); + if (currentMaterialList.Count > 0) + { + fusionTarget = currentFusionTarget; + selectedFusionMaterialList = currentMaterialList; + return; + } + } + } + + public List BrandedInRedUsing2SubFunc(List materialList, List mustMaterialList, + List> checkFuncList, Func, bool> extraCheckFunc) + { + List selectedFusionMaterialList = new List(); + + Func fusionFunc1 = checkFuncList[0]; + Func fusionFunc2 = checkFuncList[1]; + + if (mustMaterialList != null && mustMaterialList.Count > 0) + { + foreach (ClientCard mustMaterial in mustMaterialList) + { + if (!fusionFunc1(mustMaterial) && !fusionFunc2(mustMaterial)) continue; + foreach (ClientCard anotherMaterial in materialList) + { + if (anotherMaterial == mustMaterial) continue; + bool checkFlag = fusionFunc1(mustMaterial) && fusionFunc2(anotherMaterial); + checkFlag |= fusionFunc2(mustMaterial) && fusionFunc1(anotherMaterial); + checkFlag &= (extraCheckFunc == null || extraCheckFunc(new List { mustMaterial, anotherMaterial })); + if (checkFlag) + { + selectedFusionMaterialList.Add(mustMaterial); + selectedFusionMaterialList.Add(anotherMaterial); + return selectedFusionMaterialList; + } + } + } + } + else + { + for (int index1 = 0; index1 < materialList.Count - 1; ++index1) + { + ClientCard material1 = materialList[index1]; + if (!fusionFunc1(material1) && !fusionFunc2(material1)) continue; + for (int index2 = index1 + 1; index2 < materialList.Count; ++index2) + { + ClientCard material2 = materialList[index2]; + bool checkFlag = fusionFunc1(material1) && fusionFunc2(material2); + checkFlag |= fusionFunc2(material1) && fusionFunc1(material2); + checkFlag &= (extraCheckFunc == null || extraCheckFunc(new List { material1, material2 })); + if (checkFlag) + { + selectedFusionMaterial.Add(material1); + selectedFusionMaterial.Add(material2); + return selectedFusionMaterialList; + } + } + } + } + + return selectedFusionMaterial; + } + + public List BrandedInRedForChimeraFunc(List materialList, List mustMaterialList, + List> checkFuncList, Func, bool> extraCheckFunc) + { + List selectedFusionMaterialList = new List(); + + int enemyCardCount = Enemy.MonsterZone.Count(c => c != null); + enemyCardCount += Enemy.SpellZone.Count(c => c != null && c.Type != (int)CardType.Spell && c.Type != (int)CardType.Trap); + if (enemyCardCount == 0 || CheckWhetherNegated(true, true, CardType.Monster)) + { + return selectedFusionMaterialList; + } + + List fieldMaterialList = materialList.Where(c => c.Location == CardLocation.MonsterZone).OrderBy(c => c.Attack).ToList(); + List handMaterialList = materialList.Where(c => c.Location == CardLocation.Hand || c.Location == CardLocation.Grave).OrderBy(c => c.Attack).ToList(); + + // 2 field monster + 1 hand monster + if (enemyCardCount >= 2) + { + foreach (ClientCard handMonster in handMaterialList) + { + for (int fieldIndex1 = 0; fieldIndex1 < fieldMaterialList.Count - 1; ++fieldIndex1) + { + ClientCard fieldMonster1 = fieldMaterialList[fieldIndex1]; + if (fieldMonster1.IsCode(handMonster.Id) || handMonster.IsCode(fieldMonster1.Id)) continue; + for (int fieldIndex2 = fieldIndex1 + 1; fieldIndex2 < fieldMaterialList.Count; ++fieldIndex2) + { + ClientCard fieldMonster2 = fieldMaterialList[fieldIndex2]; + if (fieldMonster2.IsCode(fieldMonster1.Id) || fieldMonster1.IsCode(fieldMonster2.Id)) continue; + if (fieldMonster2.IsCode(handMonster.Id) || handMonster.IsCode(fieldMonster2.Id)) continue; + + return new List { handMonster, fieldMonster1, fieldMonster2 }; + } + } + } + } + + // 1 field monster + 2 hand monster + foreach (ClientCard fieldMonster in fieldMaterialList) + { + for (int handIndex1 = 0; handIndex1 < handMaterialList.Count - 1; ++ handIndex1) + { + ClientCard handMonster1 = handMaterialList[handIndex1]; + if (handMonster1.IsCode(fieldMonster.Id) || fieldMonster.IsCode(handMonster1.Id)) continue; + for (int handIndex2 = handIndex1 + 1; handIndex2 < handMaterialList.Count; ++handIndex2) + { + ClientCard handMonster2 = handMaterialList[handIndex2]; + if (handMonster2.IsCode(handMonster1.Id) || handMonster1.IsCode(handMonster2.Id)) continue; + if (handMonster2.IsCode(fieldMonster.Id) || fieldMonster.IsCode(handMonster2.Id)) continue; + + return new List { fieldMonster, handMonster1, handMonster2 }; + } + } + } + + return selectedFusionMaterialList; + } + + public bool BrandedLostActivate() + { + // search + if (Card.Location == CardLocation.SpellZone && Card.IsFaceup()) + { + return true; + } + + return false; + } + + public bool BrandedLostCardActivate() + { + // search + if (Card.Location == CardLocation.SpellZone && Card.IsFaceup()) return false; + + // activate + if (CheckWhetherNegated(true, true, CardType.Spell)) return false; + if (!summoned && Bot.HasInHand(CardId.FallenOfAlbaz) && CheckAlbazFusion() && Bot.Hand.Count < 3) return false; + SelectSTPlace(Card, true); + return true; + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + + ClientCard LastChainCard = Util.GetLastChainCard(); + + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ((this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || Util.IsChainTarget(Card) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + if (target != null) + { + AI.SelectCard(target); + } + else + { + AI.SelectCard(Enemy.GetMonsters()); + } + infiniteImpermanenceList.Add(this_seq); + return true; + } + } + + // negate monster + List shouldNegateList = GetMonsterListForTargetNegate(true, CardType.Trap); + if (shouldNegateList.Count > 0) + { + ClientCard negateTarget = shouldNegateList[0]; + currentNegateCardList.Add(negateTarget); + + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(negateTarget); + return true; + } + + return false; + } + + public bool BrightestBlazingBrandedKingActivate() + { + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Trap)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + if (Card.Location == CardLocation.SpellZone) + { + if (Duel.CurrentChain.Any(c => c.Controller == 0 && c.IsFaceup() && (c.Location == CardLocation.MonsterZone || c.Location == CardLocation.SpellZone) + && !c.IsCode(albazFusionMonster))) + { + return false; + } + if (Duel.CurrentChain.Any(c => c.Controller == 1 && c.IsFaceup() && !currentNegateCardList.Contains(c) && !currentDestroyCardList.Contains(c) + && (c.Location == CardLocation.MonsterZone || c.Location == CardLocation.SpellZone))) + { + currentNegateCardList.AddRange(Enemy.MonsterZone.Where(c => c != null && c.IsFaceup())); + currentNegateCardList.AddRange(Enemy.SpellZone.Where(c => c != null && c.IsFaceup())); + activatedCardIdList.Add(Card.Id); + return true; + } + } + return false; + } + + public bool BrandedBeastActivate() + { + if (CheckWhetherNegated(true, true, CardType.Trap)) return false; + + long desc = -1; + if (ActivateDescription >= Util.GetStringId(CardId.BrandedBeast, 0)) + { + desc = ActivateDescription - Util.GetStringId(CardId.BrandedBeast, 0); + } + Logger.DebugWriteLine("Beast: " + desc.ToString()); + + // destroy + if (ActivateDescription == Util.GetStringId(Card.Id, 0)) + { + ClientCard destroyTarget = null; + ClientCard releaseMonster = null; + List dangerCardList = GetProblematicEnemyCardList(true, false, CardType.Trap); + if (dangerCardList.Count > 0) + { + destroyTarget = dangerCardList[0]; + } + if (destroyTarget == null) + { + if (Duel.Player == 1) + { + if ((CurrentTiming & hintTimingMainEnd) > 0) + { + List targetList = GetNormalEnemyTargetList(true, true, CardType.Trap); + if (targetList.Count > 0) + { + destroyTarget = targetList[0]; + } + } + } + else + { + destroyTarget = Util.GetOneEnemyBetterThanMyBest(true, true); + } + } + + bool forceActivateFlag = DefaultOnBecomeTarget(); + int bystialCount = Bot.MonsterZone.Count(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeBystial)); + forceActivateFlag |= bystialCount > 0 && Duel.ChainTargets.Count(c => c.Controller == 0 && c.Location == CardLocation.MonsterZone && c.IsFaceup() && c.HasSetcode(SetcodeBystial)) == bystialCount; + forceActivateFlag |= Duel.CurrentChain.Any(c => c.Controller == 1 && c.Location == CardLocation.SpellZone && c.IsCode(_CardId.Raigeki)); + if (destroyTarget == null && forceActivateFlag) + { + releaseMonster = Duel.ChainTargets.FirstOrDefault(c => c.Controller == 0 && c.Location == CardLocation.MonsterZone && c.IsFaceup() && c.HasSetcode(SetcodeBystial)); + if (releaseMonster == null) + { + if (!activatedCardIdList.Contains(CardId.AlbionTheBrandedDragon + 1) && !sendToGYThisTurn.Any(c => c.IsCode(CardId.AlbionTheBrandedDragon))) + { + List brandedDragonList = Bot.MonsterZone.Where(c => c != null && c.IsCode(CardId.AlbionTheBrandedDragon)).OrderBy(c => c.GetDefensePower()).ToList(); + if (brandedDragonList.Count > 0) + { + releaseMonster = brandedDragonList[0]; + } + } + if (releaseMonster == null) + { + releaseMonster = Bot.MonsterZone.Where(c => c != null && c.HasRace(CardRace.Dragon) + && !(c.IsOriginalCode(CardId.FallenOfAlbaz) && Util.ChainContainsCard(CardId.FallenOfAlbaz))).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + } + } + + List targetList = GetNormalEnemyTargetList(true, true, CardType.Trap); + if (targetList.Count > 0) + { + destroyTarget = targetList[0]; + } + } + + if (destroyTarget != null) + { + if (!activatedCardIdList.Contains(CardId.AlbionTheBrandedDragon + 1) && !sendToGYThisTurn.Any(c => c.IsCode(CardId.AlbionTheBrandedDragon))) + { + List brandedDragonList = Bot.MonsterZone.Where(c => c != null && c.IsCode(CardId.AlbionTheBrandedDragon)).OrderBy(c => c.GetDefensePower()).ToList(); + if (brandedDragonList.Count > 0) + { + releaseMonster = brandedDragonList[0]; + } + } + if (releaseMonster == null) + { + releaseMonster = Bot.MonsterZone.Where(c => c != null && c.HasRace(CardRace.Dragon) + && !(c.IsOriginalCode(CardId.FallenOfAlbaz) && Util.ChainContainsCard(CardId.FallenOfAlbaz))).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + } + } + + if (releaseMonster != null && destroyTarget != null) + { + activatedCardIdList.Add(Card.Id); + AI.SelectCard(releaseMonster); + AI.SelectNextCard(destroyTarget); + currentDestroyCardList.Add(destroyTarget); + return true; + } + } + + // place + if (Duel.Phase == DuelPhase.End && Bot.HasInGraveyard(CardId.BrandedLost)) + { + activatedCardIdList.Add(Card.Id + 1); + AI.SelectCard(CardId.BrandedLost); + return true; + } + + return false; + } + + public bool BrandedRetributionActivate() + { + if (Card.Location == CardLocation.SpellZone && Duel.LastChainPlayer == 1) + { + if (CheckWhetherNegated(true, true, CardType.Trap)) return false; + bool checkFlag = Bot.Graveyard.Where(c => c != null && c.IsCode(albazFusionMonster)).Count() > 1; + checkFlag |= Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.IsCode(new[] { CardId.LubellionTheSearingDragon, CardId.SprindTheIrondashDragon, CardId.AlbaLenatusTheAbyssDragon })); + if (checkFlag) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Trap)) return false; + Dictionary> checkDict = new Dictionary>{ + {CardId.BrandedFusion, () => BrandedFusionActivateCheck()}, + {CardId.BrandedLost, () => { + if (Duel.Player == 0 && Duel.Phase >= DuelPhase.End) return false; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedFusion) && BrandedFusionActivateCheck()) return true; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedInWhite) && BrandedInWhiteActivateCheck()) return true; + if (Bot.HasInHandOrInSpellZone(CardId.BrandedInRed) && BrandedInRedActivateCheck() != null) return true; + if (!summoned && Bot.HasInHand(CardId.FallenOfAlbaz) && CheckAlbazFusion()) return true; + if ((Bot.HasInMonstersZone(CardId.BlazingCartesiaTheVirtuous) || (!summoned && Bot.HasInHand(CardId.BlazingCartesiaTheVirtuous)))) return true; + return false; + } }, + {CardId.BrandedInHighSpirits, () => !(Duel.Player == 1 && (fusionToGYFlag || Duel.Phase != DuelPhase.End)) && BrandedInHighSpiritsActivateCheck()}, + {CardId.BrandedInRed, () => BrandedInRedActivateCheck() != null }, + {CardId.BrandedInWhite, BrandedInWhiteActivateCheck }, + {CardId.BrightestBlazingBrandedKing, () => !(Duel.Player == 1 && (fusionToGYFlag || Duel.Phase != DuelPhase.End)) && Bot.GetMonsters().Any(c => c.IsFaceup() && c.IsCode(albazFusionMonster)) }, + {CardId.BrandedOpening, () => Bot.Hand.Count > 2 && !activatedCardIdList.Contains(CardId.BrandedOpening) } + }; + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = Bot.Graveyard.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + activatedCardIdList.Add(Card.Id); + AI.SelectCard(target); + return true; + } + } + } + return false; + } + + public bool GuardianChimeraActivate() + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + return true; + } + + public bool AlbionTheSanctifireDragonActivate() + { + // spsummon from grave + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + List allTargetList = Enemy.Graveyard.Where(c => c != null && c.IsMonster() && c.IsCanRevive()).ToList(); + allTargetList.AddRange(Bot.Graveyard.Where(c => c != null && c.IsMonster() && c.IsCanRevive()).ToList()); + + List targetList = new List(); + + // spsummon albaz + if (CheckAlbazFusion(null, out List materialList) && !spSummoningAlbaz) + { + bool albazFlag = materialList.Count > 1; + if (materialList.Count > 0) + { + ClientCard material = materialList[0]; + albazFlag |= material.HasType(CardType.Ritual | CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link); + albazFlag |= material.IsFloodgate() || material.IsOneForXyz() || Util.GetWorstBotMonster().GetDefensePower() < material.Attack; + albazFlag |= Duel.Player == 1 && Duel.Phase == DuelPhase.End && Duel.LastChainPlayer == -1; + } + + if (albazFlag) + { + ClientCard albaz = allTargetList.FirstOrDefault(c => c.IsOriginalCode(CardId.FallenOfAlbaz)); + ClientCard worstMonster = allTargetList.Where(c => c != albaz && !currentDestroyCardList.Contains(c)).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (albaz != null && worstMonster != null + && (GetProblematicEnemyMonster(0, false, false, CardType.Monster) != null || Math.Max(worstMonster.Attack, worstMonster.Defense) <= albaz.Defense)) + { + Logger.DebugWriteLine("Sanctifire 1"); + targetList.AddRange(new[] { albaz, worstMonster }); + spSummoningAlbaz = true; + } + } + } + + // spsummon floogate + if (targetList.Count == 0) + { + ClientCard floogateCard = allTargetList.Where(c => c.IsFloodgate()).OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (floogateCard != null) + { + // select worst monster + ClientCard worstMonster = allTargetList.Where(c => c != floogateCard).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (worstMonster != null) + { + Logger.DebugWriteLine("Sanctifire 2"); + targetList.AddRange(new[] { floogateCard, worstMonster }); + } + } + } + + // spsummon target + if (targetList.Count == 0 && Duel.LastChainPlayer == 1) + { + List targetedList = Duel.LastChainTargets.Intersect(allTargetList).ToList(); + if (targetedList.Count > 0) + { + ClientCard target = ShuffleList(targetedList)[0]; + ClientCard anotherTarget = null; + if (target.GetDefensePower() >= 2000) + { + // select worst + anotherTarget = allTargetList.Where(c => c != target).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + } else + { + // select best + anotherTarget = allTargetList.Where(c => c != target).OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + } + if (anotherTarget != null) + { + Logger.DebugWriteLine("Sanctifire 3"); + targetList.AddRange(new[] { target, anotherTarget }); + } + } + } + + // spsummon useful monster + if (targetList.Count == 0 && Duel.Player == 1 && Duel.Phase == DuelPhase.End) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.BorreloadFuriousDragon, () => Enemy.GetSpellCount() > 0 }, + {CardId.MirrorjadeTheIcebladeDragon, () => !Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) && !Bot.HasInSpellZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) }, + {CardId.AluberTheJesterOfDespia, () => !activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) }, + {CardId.SpringansKitt, () => !activatedCardIdList.Contains(CardId.SpringansKitt + 1) }, + }; + + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = allTargetList.FirstOrDefault(c => c.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + // select worst + ClientCard anotherTarget = allTargetList.Where(c => c != target).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + targetList.Add(target); + targetList.Add(anotherTarget); + Logger.DebugWriteLine("Sanctifire 4"); + break; + } + } + } + + // avoid evenly match + if (targetList.Count == 0 && Duel.Player == 1 && Enemy.Hand.Count > 0 && Enemy.GetMonsterCount() + Enemy.GetSpellCount() == 0 && (CurrentTiming & hintBattleStart) > 0) + { + ClientCard summonTarget = allTargetList.OrderByDescending(c => c.Attack).FirstOrDefault(); + Dictionary> checkDict = new Dictionary> + { + {CardId.MirrorjadeTheIcebladeDragon, () => !Bot.HasInMonstersZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) && !Bot.HasInSpellZone(CardId.MirrorjadeTheIcebladeDragon, faceUp: true) }, + {CardId.AluberTheJesterOfDespia, () => !activatedCardIdList.Contains(CardId.AluberTheJesterOfDespia) }, + }; + foreach (KeyValuePair> pair in checkDict) + { + ClientCard target = allTargetList.FirstOrDefault(c => c.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + summonTarget = target; + break; + } + } + if (summonTarget != null) + { + // select worst + ClientCard anotherTarget = allTargetList.Where(c => c != summonTarget).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + targetList.Add(summonTarget); + targetList.Add(anotherTarget); + Logger.DebugWriteLine("Sanctifire 5"); + } + } + + if (targetList.Count > 0) + { + AI.SelectMaterials(targetList, HintMsg.SpSummon); + currentDestroyCardList.AddRange(targetList); + activatedCardIdList.Add(Card.Id); + return true; + } + } + // spsummon itself + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + List botCards = new List { Bot.MonsterZone[2], Bot.MonsterZone[5], Bot.MonsterZone[6] }; + List enemyCards = new List { Enemy.MonsterZone[2], Enemy.MonsterZone[5], Enemy.MonsterZone[6] }; + if (enemyCards.Any(c => c != null && (c.IsFloodgate() || c.IsMonsterDangerous()))) return true; + return botCards.Select(c => c == null ? 0 : c.GetDefensePower()).Sum() < enemyCards.Select(c => c == null ? 0 : c.GetDefensePower()).Sum(); + } + return false; + } + + public bool MirrorjadeTheIcebladeDragonActivate() + { + if (Card.Location != CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + return true; + } + if (Card.Location == CardLocation.MonsterZone) + { + bool checkFlag = GetProblematicEnemyMonster(0, false, false, CardType.Monster) != null; + if (Enemy.GetMonsterCount() > 0) + { + checkFlag |= Duel.Player == 1 && Duel.Phase == DuelPhase.End && Duel.LastChainPlayer != 0; + int enemyBattlePower = Enemy.BattlingMonster == null ? 0 : Enemy.BattlingMonster.GetDefensePower(); + int botBattlePower = Bot.BattlingMonster == null ? 0 : Bot.BattlingMonster.GetDefensePower(); + checkFlag |= enemyBattlePower > 0 && enemyBattlePower > botBattlePower && Duel.LastChainPlayer != 0 && !currentDestroyCardList.Contains(Enemy.BattlingMonster); + checkFlag |= DefaultOnBecomeTarget() && Duel.LastChainPlayer != 0; + } + if (Duel.CurrentChain.Any(c => c.IsCode(CardId.NibiruThePrimalBeing) && !DefaultCheckWhetherCardIdIsNegated(CardId.NibiruThePrimalBeing))) + { + checkFlag |= Enemy.GetMonsterCount() > 0; + checkFlag |= Bot.HasInMonstersZone(new[] { CardId.TheBystialLubellion, CardId.DespianTragedy, CardId.TriBrigadeMercourier }); + } + + if (checkFlag) + { + Dictionary> checkDict = new Dictionary> + { + {CardId.AlbionTheBrandedDragon, + () =>!sendToGYThisTurn.Any(c => c.IsCode(CardId.AlbionTheBrandedDragon)) && !DefaultCheckWhetherCardIdIsNegated(CardId.AlbionTheBrandedDragon) + && !(Duel.Player == 0 && Bot.HasInMonstersZone(CardId.AlbionTheBrandedDragon) && Bot.HasInGraveyard(CardId.TheBystialLubellion))}, + {CardId.RindbrummTheStrikingDragon, + () => Duel.Player == 1 && Bot.Graveyard.Any(c => c.IsOriginalCode(CardId.FallenOfAlbaz)) && Bot.Hand.Count > 0 + && !activatedCardIdList.Contains(CardId.FallenOfAlbaz) && !DefaultCheckWhetherCardIdIsNegated(CardId.FallenOfAlbaz)}, + {CardId.TitanikladTheAshDragon, () => CheckRemainInDeck(CardId.GuidingQuemTheVirtuous) > 0}, + {CardId.SprindTheIrondashDragon, () => CheckRemainInDeck(CardId.SpringansKitt) > 0}, + {CardId.AlbaLenatusTheAbyssDragon, () => CheckRemainInDeck(CardId.FusionDeployment, CardId.BrandedFusion) > 0 }, + {CardId.LubellionTheSearingDragon, () => true }, + {CardId.AlbionTheSanctifireDragon, () => true } + }; + + foreach (KeyValuePair> pair in checkDict) + { + if (Bot.HasInExtra(pair.Key) && pair.Value()) + { + AI.SelectCard(pair.Key); + return true; + } + } + } + } + return false; + } + + public bool BorreloadFuriousDragonActivate() + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + + // destroy dangerous card + List dangerList = GetProblematicEnemyCardList(true, false, CardType.Monster); + if (dangerList.Count > 0) + { + ClientCard botTarget = Bot.GetMonsters().OrderBy(c => c.GetDefensePower() + (c.IsCode(albazFusionMonster) ? 1 : 0)).FirstOrDefault(); + if (botTarget != null) + { + AI.SelectCard(botTarget); + AI.SelectNextCard(dangerList); + currentDestroyCardList.Add(botTarget); + currentDestroyCardList.Add(dangerList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + List enemyTargetList = GetNormalEnemyTargetList(true, true, CardType.Monster); + + // become target + if (Duel.LastChainPlayer == 1) + { + List targetedBotMonsterList = Duel.LastChainTargets.Where(c => c.Location == CardLocation.MonsterZone && c.Controller == 0).ToList(); + ClientCard lastChainCard = Util.GetLastChainCard(); + // if it's a negate effect, only destroy not important monster + if (lastChainCard != null && lastChainCard.IsCode(targetNegateIdList)) + { + targetedBotMonsterList = targetedBotMonsterList.Where(c => !c.IsCode(CardId.BlazingCartesiaTheVirtuous) || c.Attack < 2500).OrderBy(c => c.Attack).ToList(); + } + if (targetedBotMonsterList.Count > 0) + { + AI.SelectCard(targetedBotMonsterList); + AI.SelectNextCard(enemyTargetList); + currentDestroyCardList.Add(targetedBotMonsterList[0]); + currentDestroyCardList.Add(enemyTargetList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + // end phase + if (Duel.Player == 1 && Duel.Phase == DuelPhase.End) + { + List botTargetList = Bot.MonsterZone.Where(c => c != null && c.GetDefensePower() <= 2500).OrderBy(c => c.GetDefensePower()).ToList(); + if (botTargetList.Count > 0) + { + AI.SelectCard(botTargetList); + AI.SelectNextCard(enemyTargetList); + currentDestroyCardList.Add(botTargetList[0]); + currentDestroyCardList.Add(enemyTargetList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + return false; + } + + public bool LubellionTheSearingDragonActivate() + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + if (Card.Location == CardLocation.MonsterZone) + { + int fusionTarget = LubellionTheSearingDragonFusionTarget(Bot.ExtraDeck, out _); + if (fusionTarget > 0) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + return false; + } + + public int LubellionTheSearingDragonFusionTarget(IList cards, out ClientCard target) + { + target = null; + bool hasAlbaz = Bot.Banished.Any(c => c != null && c.IsFaceup() && c.IsOriginalCode(CardId.FallenOfAlbaz)); + hasAlbaz |= Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.IsOriginalCode(CardId.FallenOfAlbaz)); + Dictionary> checkDict = new Dictionary> + { + {CardId.MirrorjadeTheIcebladeDragon, () => hasAlbaz && !DefaultCheckWhetherCardIdIsNegated(CardId.MirrorjadeTheIcebladeDragon) }, + {CardId.GranguignolTheDuskDragon, () => Bot.HasInBanished(CardId.BlazingCartesiaTheVirtuous) }, + {CardId.BorreloadFuriousDragon, () => + { + int darkDragonCount = Bot.Banished.Where(c => c.IsFaceup() && c.HasRace(CardRace.Dragon) && c.HasAttribute(CardAttribute.Dark) + && !c.IsCode(cannotBeFusionMaterialIdList) ).Count(); + darkDragonCount += Bot.Graveyard.Where(c => c.HasRace(CardRace.Dragon) && c.HasAttribute(CardAttribute.Dark) + && !c.IsCode(cannotBeFusionMaterialIdList) + && (Duel.Player == 1 || !CheckWhetherShouldKeepInGrave(c)) ).Count(); + return darkDragonCount >= 2; + } }, + {CardId.RindbrummTheStrikingDragon, () => hasAlbaz && Bot.HasInBanished(CardId.TriBrigadeMercourier) }, + {CardId.TitanikladTheAshDragon, () => + { + if (!hasAlbaz) return false; + ClientCard enemyMonster = Util.GetBestEnemyMonster(true); + ClientCard botMonster = Util.GetBestBotMonster(true); + int enemyPower = enemyMonster == null ? 0 : enemyMonster.GetDefensePower(); + int botPower = botMonster == null ? 0 : botMonster.Attack; + if (enemyPower > 0 && enemyPower >= botPower) + { + List materialList = new List(Bot.Banished); + materialList.AddRange(Bot.Graveyard); + foreach (ClientCard material in materialList) + { + if (material != null && material.IsFaceup() && material.Attack >= 2500 && (2900 + material.Level >= enemyPower)) + { + return true; + } + } + } + return false; + } }, + {CardId.DespianQuaeritis, () => { + if (!Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && c.Attack > 2500 && !(c.HasType(CardType.Fusion) && c.Level >= 8))) return false; + bool checkFlag = Bot.Banished.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeDespain)); + checkFlag |= Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeDespain)); + + return checkFlag; + } }, + {CardId.AlbaLenatusTheAbyssDragon, () => hasAlbaz && !Bot.HasInExtra(CardId.AlbionTheBrandedDragon) }, + {CardId.AlbionTheBrandedDragon, () => { + if (activatedCardIdList.Contains(CardId.AlbionTheBrandedDragon) || DefaultCheckWhetherCardIdIsNegated(CardId.AlbionTheBrandedDragon)) return false; + bool checkFlag = Bot.Banished.Any(c => c != null && c.IsFaceup() && c.HasAttribute(CardAttribute.Light)); + checkFlag |= Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.HasAttribute(CardAttribute.Light)); + + return checkFlag; + }} + }; + + foreach (KeyValuePair> pair in checkDict) + { + target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return pair.Key; + } + } + + target = null; + return 0; + } + + public bool AlbaLenatusTheAbyssDragonSpSummon() + { + // use albaz + enemy's dragon monster + List enemyDragon = Enemy.GetMonsters().Where(c => c != null && c.IsFaceup() && !c.IsCode(cannotBeFusionMaterialIdList) && c.HasRace(CardRace.Dragon)).ToList(); + if (enemyDragon.Count > 0) + { + bool successFlag = enemyDragon.Count > 1; + int bestBotPower = Util.GetBestAttack(Bot); + successFlag |= enemyDragon.Any(c => c.GetDefensePower() >= bestBotPower); + if (successFlag) + { + if (!enemyDragon.Any(c => c.IsCode(CardId.FallenOfAlbaz))) + { + ClientCard botAlbaz = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsCode(CardId.FallenOfAlbaz)); + if (botAlbaz != null) + { + enemyDragon.Add(botAlbaz); + } + } + AI.SelectMaterials(enemyDragon); + return true; + } + } + + return false; + } + + public bool AlbaLenatusTheAbyssDragonActivate() + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool GranguignolTheDuskDragonActivate() + { + long desc = -1; + if (ActivateDescription >= Util.GetStringId(CardId.GranguignolTheDuskDragon, 0)) + { + desc = ActivateDescription - Util.GetStringId(CardId.GranguignolTheDuskDragon, 0); + } + Logger.DebugWriteLine("granguignol: " + desc.ToString()); + + // send to GY + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.GranguignolTheDuskDragon, 0)) + { + if (CheckWhetherNegated(true, true, CardType.Monster) || CheckWhetherWillbeRemoved()) return false; + int checkId = GranguignolTheDuskDragonSendToGYTarget(null, out _); + if (checkId > 0) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + // spsummon + if (ActivateDescription == Util.GetStringId(CardId.GranguignolTheDuskDragon, 1)) + { + if (CheckWhetherNegated(true, Card.Location == CardLocation.MonsterZone, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public int GranguignolTheDuskDragonSendToGYTarget(IList cards, out ClientCard target) + { + bool needSendBranded = Bot.HasInGraveyard(CardId.BrandedRetribution) && CheckRemainInDeck(CardId.BrandedFusion) > 0; + if (CheckRemainInDeck(CardId.BrandedRetribution) > 0) + { + needSendBranded |= Bot.Graveyard.Any(c => c != null && c.HasType(CardType.Spell | CardType.Trap) && c.HasSetcode(SetcodeBranded) + && !(fusionToGYFlag && c.IsCode(CardId.BrightestBlazingBrandedKing, CardId.BrandedInHighSpirits))); + needSendBranded |= !activatedCardIdList.Contains(CardId.AlbionTheShroudedDragon) && !CheckWhetherWillbeRemoved() && Bot.HasInHandOrInGraveyard(CardId.AlbionTheShroudedDragon); + needSendBranded |= Duel.CurrentChain.Any(c => c.Controller == 0 && c.Location == CardLocation.Grave && c.IsCode(CardId.BystialSaronir)); + } + List>> checkList = new List>> + { + new KeyValuePair>(CardId.BystialSaronir, () => !activatedCardIdList.Contains(CardId.BystialSaronir) && needSendBranded), + new KeyValuePair>(CardId.AlbionTheShroudedDragon, () => !activatedCardIdList.Contains(CardId.AlbionTheShroudedDragon) && needSendBranded ), + new KeyValuePair>(CardId.AlbionTheBrandedDragon, () => !activatedCardIdList.Contains(CardId.AlbionTheBrandedDragon + 1) && !sendToGYThisTurn.Any(c => c.IsCode(CardId.AlbionTheBrandedDragon)) ), + new KeyValuePair>(CardId.TitanikladTheAshDragon, () => !activatedCardIdList.Contains(CardId.TitanikladTheAshDragon) && !sendToGYThisTurn.Any(c => c.IsCode(CardId.TitanikladTheAshDragon)) + && CheckRemainInDeck(CardId.GuidingQuemTheVirtuous, CardId.FallenOfAlbaz) > 0), + new KeyValuePair>(CardId.TheBystialLubellion, () => Bot.HasInMonstersZone(new[] {CardId.AlbionTheBrandedDragon, CardId.TitanikladTheAshDragon}) + && CheckRemainInDeck(CardId.BrandedLost, CardId.BrandedBeast) > 0 ), + new KeyValuePair>(CardId.SprindTheIrondashDragon, () => !activatedCardIdList.Contains(CardId.SprindTheIrondashDragon) && !sendToGYThisTurn.Any(c => c.IsCode(CardId.SprindTheIrondashDragon)) + && CheckRemainInDeck(CardId.SpringansKitt) > 0), + new KeyValuePair>(CardId.DespianLuluwalilith, () => CheckRemainInDeck(CardId.BlazingCartesiaTheVirtuous, CardId.GuidingQuemTheVirtuous) > 0 ), + new KeyValuePair>(CardId.AlbionTheShroudedDragon, () => true ), + }; + + foreach (KeyValuePair> pair in checkList) + { + if (cards == null) + { + if ((CheckRemainInDeck(pair.Key) > 0 || Bot.HasInExtra(pair.Key)) && pair.Value()) + { + target = null; + return pair.Key; + } + } else + { + ClientCard tg = cards.FirstOrDefault(c => c.IsOriginalCode(pair.Key)); + if (tg != null && pair.Value()) + { + target = tg; + return pair.Key; + } + } + } + + target = null; + return 0; + } + + public bool DespianQuaeritisActivate() + { + if (Card.Location == CardLocation.MonsterZone && Duel.Phase == DuelPhase.Main1 && !CheckWhetherNegated(true, true, CardType.Monster)) + { + if ((CurrentTiming & hintTimingMainEnd) != 0 && Duel.Player == 1) + { + int bestBotPower = Util.GetBestPower(Bot, false); + if (Enemy.GetMonsters().Any(c => c.GetDefensePower() >= bestBotPower && c.IsAttack() && !(c.HasType(CardType.Fusion) && c.Level >= 8))) { + activatedCardIdList.Add(Card.Id); + return true; + } + } + if (Duel.Player == 0 && Enemy.GetMonsters().Any(c => c.IsAttack() && !(c.HasType(CardType.Fusion) && c.Level >= 8))) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + if (Card.Location != CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return true; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + return false; + } + + public bool SprindTheIrondashDragonActivate() + { + // search + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + // destroy + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + int moveDest = SprindTheIrondashDragonMoveZone(0, Card); + if (moveDest > 0) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + return false; + } + + public int SprindTheIrondashDragonMoveZone(int available = 0, ClientCard selfCard = null) + { + int maxZone = -1; + int maxValue = 0; + for (int zoneId = 0; zoneId < 5; ++zoneId) + { + // check whether can move + if (Bot.MonsterZone[zoneId] != null) continue; + int zone = (int)System.Math.Pow(2, zoneId); + if (available > 0 && (available & zone) == 0) continue; + + int currentValue = SprindTheIrondashDragonDestroyValue(zoneId, selfCard); + if (currentValue > maxValue) + { + maxZone = zone; + maxValue = currentValue; + } + } + return maxZone; + } + + public int SprindTheIrondashDragonDestroyValue(int zoneId, ClientCard selfCard = null) + { + int value = 0; + if (zoneId == 1 || zoneId == 3) + { + ClientCard botMonsterInExtraZone = Bot.MonsterZone[(zoneId + 9) / 2]; + if (botMonsterInExtraZone != null && botMonsterInExtraZone != selfCard && botMonsterInExtraZone.IsFaceup()) value -= 5; + + ClientCard enemyMonserInExtraZone = Enemy.MonsterZone[(11 - zoneId) / 2]; + if (enemyMonserInExtraZone != null && enemyMonserInExtraZone.IsFaceup()) value += 2; + } + ClientCard botSpell = Bot.SpellZone[zoneId]; + if (botSpell != null && botSpell.IsFaceup()) value--; + ClientCard enemyMonster = Enemy.MonsterZone[5 - zoneId]; + if (enemyMonster != null && enemyMonster.IsFaceup()) + { + value++; + if (enemyMonster.IsFloodgate() || enemyMonster.IsMonsterDangerous()) value += 5; + } + ClientCard enemySpell = Enemy.SpellZone[5 - zoneId]; + if (enemySpell != null && enemySpell.IsFaceup()) + { + value++; + if (enemySpell.IsFloodgate()) value += 5; + } + + return value; + } + + public bool TitanikladTheAshDragonActivate() + { + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + return false; + } + + public bool RindbrummTheStrikingDragonActivate() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + bool checkFlag = false; + + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null) + { + checkFlag = Duel.LastChainPlayer == 1; + checkFlag |= Duel.LastChainPlayer == 0 && lastChainCard.IsCode(CardId.MirrorjadeTheIcebladeDragon) && lastChainCard.Location == CardLocation.MonsterZone && Enemy.GetMonsterCount() == 0; + } + + if (checkFlag) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, true, CardType.Monster) || Duel.CurrentChain.Any(c => c.Controller == 0 && c.IsCode(CardId.GuidingQuemTheVirtuous))) return false; + + ClientCard albaz = Bot.Graveyard.FirstOrDefault(c => c.IsOriginalCode(CardId.FallenOfAlbaz)); + bool checkFlag = Card.IsCanRevive() && Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)); + bool albazFlag = CheckAlbazFusion(null, out List materialList) && albaz != null && !spSummoningAlbaz; + if (albazFlag) + { + checkFlag |= materialList.Count > 1; + if (materialList.Count > 0) + { + ClientCard material = materialList[0]; + checkFlag |= material.HasType(CardType.Ritual | CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link); + checkFlag |= material.IsFloodgate() || material.IsOneForXyz() || Util.GetWorstBotMonster()?.GetDefensePower() < material.Attack; + checkFlag |= Duel.Player == 1 && Duel.Phase == DuelPhase.End && Duel.LastChainPlayer == -1; + } + } + + if (checkFlag) + { + albaz = Bot.Graveyard.Where(c => c.IsCode(CardId.FallenOfAlbaz)).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + + if (checkFlag && albaz != null) + { + spSummoningAlbaz = true; + activatedCardIdList.Add(Card.Id + 1); + AI.SelectCard(albaz); + return true; + } + } + } + return false; + } + + public bool AlbionTheBrandedDragonActivate() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + int fusionTarget = AlbionTheBrandedDragonFusionTarget(Bot.ExtraDeck, out _); + if (fusionTarget > 0) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + return false; + } + + public int AlbionTheBrandedDragonFusionTarget(IList cards, out ClientCard target) + { + target = null; + Dictionary> checkDict = new Dictionary> + { + {CardId.MirrorjadeTheIcebladeDragon, () => { + bool checkFlag = !CheckWhetherNegated() && CheckShouldNoMoreSpSummon(); + checkFlag |= Bot.Graveyard.Any(c => c != null && !sendToGYThisTurn.Contains(c) && !c.IsCode(cannotBeFusionMaterialIdList) + && c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)); + return checkFlag; + } }, + {CardId.LubellionTheSearingDragon, () => + { + if (!activatedCardIdList.Contains(CardId.LubellionTheSearingDragon)) + { + List checkMaterialList = new List(Bot.GetMonsters()); + checkMaterialList.AddRange(Bot.Graveyard); + bool albazChecked = false; + bool hasOriginalAlbaz = checkMaterialList.Any(c => c.IsOriginalCode(CardId.FallenOfAlbaz)); + foreach (ClientCard checkCard in checkMaterialList) + { + if (!albazChecked && checkCard.IsCode(CardId.FallenOfAlbaz) && (!hasOriginalAlbaz || !checkCard.IsOriginalCode(CardId.AlbionTheShroudedDragon))) + { + albazChecked = true; + continue; + } + if (checkCard.HasAttribute(CardAttribute.Dark)) return true; + } + if (Bot.HasInHand(CardId.TriBrigadeMercourier) && Bot.Hand.Count >= 2) return true; + } + return false; + } }, + {CardId.BorreloadFuriousDragon, () => { + if (Enemy.GetMonsterCount() + Enemy.GetSpellCount() > 0) + { + int darkDragonCount = Bot.Graveyard.Where(c => c.HasRace(CardRace.Dragon) && c.HasAttribute(CardAttribute.Dark) + && !c.IsCode(cannotBeFusionMaterialIdList) + && (Duel.Player == 1 || !CheckWhetherShouldKeepInGrave(c)) ).Count(); + if (Duel.Player == 1 && Bot.GetMonsters().Any(c => c.HasRace(CardRace.Dragon) && c.HasAttribute(CardAttribute.Dark) && !c.IsCode(cannotBeFusionMaterialIdList))) { + darkDragonCount ++; + } + return darkDragonCount >= 2; + } + return false; + } }, + {CardId.AlbionTheSanctifireDragon, () => + { + ClientCard albaz = Bot.Graveyard.FirstOrDefault(c => c.IsCode(CardId.FallenOfAlbaz)); + ClientCard lightSpellcaster = Bot.Graveyard.FirstOrDefault(c => c.HasRace(CardRace.SpellCaster) && c.HasAttribute(CardAttribute.Light)); + int remainMonsterCount = Enemy.GetGraveyardMonsters().Count; + remainMonsterCount += Bot.Graveyard.Where(c => c.IsMonster() && c != albaz && c != lightSpellcaster).Count(); + remainMonsterCount += Bot.HasInHand(_CardId.MaxxC) ? 1 : 0; + return remainMonsterCount >= 2; + } }, + {CardId.RindbrummTheStrikingDragon, () => Bot.HasInGraveyard(CardId.TriBrigadeMercourier) }, + {CardId.DespianQuaeritis, () => + { + bool checkFlag = Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && c.Attack >= 2500 && !(c.HasType(CardType.Fusion) && c.Level >= 8)); + if (checkFlag) + { + ClientCard despian = Bot.Graveyard.Where(c => c != null && c.HasSetcode(SetcodeDespain) && !CheckWhetherShouldKeepInGrave(c)) + .OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (despian == null) + { + despian = Bot.MonsterZone.Where(c => c != null && c.HasSetcode(SetcodeDespain)).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + } + if (despian != null) + { + return Bot.Graveyard.Any(c => c.HasAttribute(CardAttribute.Light | CardAttribute.Dark) && !CheckWhetherShouldKeepInGrave(c) && c != despian); + } + } + + return false; + } }, + {CardId.TitanikladTheAshDragon, () => + { + ClientCard albaz = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsOriginalCode(CardId.FallenOfAlbaz)); + if (albaz == null) + { + albaz = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsCode(CardId.FallenOfAlbaz)); + } + foreach (ClientCard material in Bot.Graveyard) + { + if (material != null && material != albaz && material.IsMonster() && material.Attack >= 2500 && !material.IsCode(cannotBeFusionMaterialIdList)) + { + bool checkFlag = !Util.IsTurn1OrMain2() && Enemy.GetMonsterCount() == 0; + checkFlag |= !CheckWhetherShouldKeepInGrave(material); + return checkFlag; + } + } + return false; + } }, + {CardId.AlbaLenatusTheAbyssDragon, () => + { + if (Util.GetOneEnemyBetterThanMyBest() == null) + { + ClientCard albaz = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsOriginalCode(CardId.FallenOfAlbaz)); + if (albaz == null) + { + albaz = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsCode(CardId.FallenOfAlbaz)); + } + foreach (ClientCard material in Bot.Graveyard) + { + if (material != null && material != albaz && material.IsMonster() && material.HasRace(CardRace.Dragon) && !material.IsCode(cannotBeFusionMaterialIdList)) + { + return true; + } + } + } + return false; + } }, + {CardId.GranguignolTheDuskDragon, () => + { + if (cards == null) return false; + return true; + } } + }; + + foreach (KeyValuePair> pair in checkDict) + { + target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + return pair.Key; + } + } + + target = null; + return 0; + } + + public bool DespianLuluwalilithActivate() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated(true, false, CardType.Monster)) return false; + if (CheckRemainInDeck(CardId.GuidingQuemTheVirtuous, CardId.BlazingCartesiaTheVirtuous) > 0) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + return false; + } + + public bool SetForChimera() + { + if (Card.Level <= 4) return false; + if (Bot.GetMonsterCount() > 0 || !Bot.HasInHandOrInSpellZone(CardId.BrandedInWhite) || !Bot.HasInExtra(CardId.GuardianChimera)) return false; + if (DefaultCheckWhetherCardIdIsNegated(CardId.GuardianChimera) || CheckWhetherNegated(true, true, CardType.Monster)) return false; + if (Enemy.MonsterZone.All(c => c == null) && Enemy.SpellZone.All(c => c == null)) return false; + + for (int handIndex1 = 0; handIndex1 < Bot.Hand.Count - 1; ++ handIndex1) + { + ClientCard hand1 = Bot.Hand[handIndex1]; + if (!hand1.IsMonster() || hand1.IsCode(Card.Id) || Card.IsCode(hand1.Id)) continue; + for (int handIndex2 = handIndex1 + 1; handIndex2 < Bot.Hand.Count; ++ handIndex2) + { + ClientCard hand2 = Bot.Hand[handIndex2]; + if (!hand2.IsMonster()) continue; + if (hand2.IsCode(Card.Id) || Card.IsCode(hand2.Id) || hand2.IsCode(hand1.Id) || hand1.IsCode(hand2.Id)) continue; + bool checkFlag = Card.HasRace(CardRace.Dragon) || hand1.HasRace(CardRace.Dragon) || hand2.HasRace(CardRace.Dragon); + if (checkFlag) + { + summoned = true; + return true; + } + } + } + + return false; + } + + public bool AdvanceSummon() + { + if (Card.Level < 5) return false; + + List releaseGoal = Bot.MonsterZone.Where(c => c != null && c.IsFaceup() && !c.IsDisabled() && c.IsCode(10158145)).ToList(); + if (releaseGoal.Count > 0) + { + if (Card.Level <= 6) + { + AI.SelectMaterials(releaseGoal); + summoned = true; + return true; + } + + if (Card.Level >= 7) + { + if (releaseGoal.Count < 2) + { + ClientCard anotherMaterial = Bot.MonsterZone.Where(c => c != null && !releaseGoal.Contains(c)).OrderBy(c => c.GetDefensePower()).FirstOrDefault(); + if (anotherMaterial.GetDefensePower() > Card.Attack) return false; + releaseGoal.Add(anotherMaterial); + } + if (releaseGoal.Count >= 2) + { + AI.SelectMaterials(releaseGoal); + summoned = true; + return true; + } + } + } + return false; + } + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + switch (Card.Id) + { + case CardId.BrandedInHighSpirits: + { + bool checkFlag = (Bot.HasInMonstersZone(CardId.GuidingQuemTheVirtuous) + || (CheckRemainInDeck(CardId.GuidingQuemTheVirtuous) > 0 && sendToGYThisTurn.Any(c => c.IsCode(CardId.TitanikladTheAshDragon)))); + if (!checkFlag) return false; + } + break; + case CardId.BrandedOpening: + { + bool checkFlag = CheckRemainInDeck(CardId.AluberTheJesterOfDespia, CardId.GuidingQuemTheVirtuous) > 0; + if (!checkFlag) return false; + } + break; + case CardId.BrightestBlazingBrandedKing: + { + bool checkFlag = Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.IsCode(albazFusionMonster)); + if (!checkFlag) return false; + } + break; + case CardId.BrandedBeast: + { + bool checkFlag = Bot.HasInGraveyard(CardId.BrandedLost) || Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeBystial)); + checkFlag |= Bot.HasInHand(CardId.BystialSaronir) && (Enemy.Graveyard.Any(c => CheckBystialCanBanish(c)) || Bot.Graveyard.Any(c => CheckBystialCanBanish(c))); + if (!checkFlag) return false; + } + break; + case CardId.BrandedRetribution: + { + bool checkFlag = Bot.Graveyard.Where(c => c.IsCode(albazFusionMonster)).Count() > 1; + checkFlag |= Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.IsCode(albazFusionMonster)); + if (!checkFlag) return false; + } + break; + default: + break; + } + + // select place + if ((Card.IsTrap() || Card.HasType(CardType.QuickPlay))) + { + List avoid_list = new List(); + int setForInfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setForInfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setForInfiniteImpermanence); + return true; + } + else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } + else + { + SelectSTPlace(); + } + return true; + } + + else if (Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true) || Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true)) + { + if (Card.IsSpell() && !Bot.HasInSpellZone(Card.Id)) + { + SelectSTPlace(); + return true; + } + } + + return false; + } + + protected override bool DefaultSetForDiabellze() + { + if (base.DefaultSetForDiabellze()) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + // for Sanctifire Dragon spsummoned monster + public bool FloogateActivate() + { + if (Card.Owner != 1 && !Card.IsFloodgate()) return false; + if (CheckWhetherNegated()) return false; + if (Executors.Any(e => e != null && e.Type == ExecutorType.Activate && e.CardId == Card.Id)) return false; + if (Duel.LastChainPlayer == 1) + { + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && lastChainCard.IsFaceup() && CheckCanBeTargeted(lastChainCard, true, CardType.Monster) + && (lastChainCard.Location == CardLocation.MonsterZone || lastChainCard.Location == CardLocation.SpellZone)) + { + AI.SelectCard(lastChainCard); + } + } + + return Duel.LastChainPlayer == 1; + } + } +} diff --git a/Game/AI/Decks/AltergeistExecutor.cs b/Game/AI/Decks/AltergeistExecutor.cs index aa06a525..909df654 100644 --- a/Game/AI/Decks/AltergeistExecutor.cs +++ b/Game/AI/Decks/AltergeistExecutor.cs @@ -1,8 +1,9 @@ -using YGOSharp.OCGWrapper.Enums; +using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using WindBot; using WindBot.Game; using WindBot.Game.AI; +using System.Linq; namespace WindBot.Game.AI.Decks { @@ -324,23 +325,37 @@ public int GetTotalATK(IList list) public int SelectSTPlace(ClientCard card=null, bool avoid_Impermanence = false) { - List list = new List { 0, 1, 2, 3, 4 }; + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoid_Impermanence && Impermanence_list.Contains(seq)) continue; + list.Add(seq); + } + } int n = list.Count; while (n-- > 1) { - int index = Program.Rand.Next(n + 1); - int temp = list[index]; - list[index] = list[n]; - list[n] = temp; + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; } - foreach (int seq in list) + if (avoid_Impermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) { - int zone = (int)System.Math.Pow(2, seq); - if (Bot.SpellZone[seq] == null) + foreach (int seq in list) { - if (card != null && card.Location == CardLocation.Hand && avoid_Impermanence && Impermanence_list.Contains(seq)) continue; - return zone; - }; + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + return (int)System.Math.Pow(2, seq); + } + } + foreach (int seq in list) + { + return (int)System.Math.Pow(2, seq); } return 0; } @@ -376,6 +391,7 @@ public int SelectSetPlace(List avoid_list=null) public bool spell_trap_activate(bool isCounter = false, ClientCard target = null) { if (target == null) target = Card; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return true; if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !Bot.HasInHandOrHasInMonstersZone(CardId.GO_SR) && !isCounter && !Bot.HasInSpellZone(CardId.SolemnStrike)) return false; if (target.IsSpell()) @@ -649,7 +665,7 @@ public bool SecretVillage_activate() public bool G_activate() { - return (Duel.Player == 1); + return (Duel.Player == 1) && !DefaultCheckWhetherCardIsNegated(Card); } public bool NaturalExterio_eff() @@ -806,6 +822,7 @@ public bool Impermanence_activate() public bool Hand_act_eff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Card.IsCode(CardId.AB_JS) && Util.GetLastChainCard().HasSetcode(0x11e) && Util.GetLastChainCard().Location == CardLocation.Hand) // Danger! archtype hand effect return false; if (Card.IsCode(CardId.GO_SR) && Card.Location == CardLocation.Hand && Bot.HasInMonstersZone(CardId.GO_SR)) return false; @@ -886,6 +903,7 @@ public bool WakingtheDragon_eff() public bool GR_WC_activate() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; int warrior_count = 0; int pendulum_count = 0; int link_count = 0; @@ -2654,6 +2672,28 @@ public override void OnNewTurn() ss_other_monster = false; Impermanence_list.Clear(); attacked_Meluseek.Clear(); + base.OnNewTurn(); + } + + public override void OnChaining(int player, ClientCard card) + { + if (card == null) return; + + if (player == 1) + { + if (card.IsCode(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + Impermanence_list.Add(4-i); + break; + } + } + } + } + base.OnChaining(player, card); } public bool MonsterRepos() @@ -2797,7 +2837,8 @@ public override IList OnSelectCard(IList cards, int min, // throw all?? return null; } - return null; + + return base.OnSelectCard(cards, min, max, hint, cancelable); } public override CardPosition OnSelectPosition(int cardId, IList positions) @@ -2876,5 +2917,15 @@ public override int OnSelectPlace(long cardId, int player, CardLocation location } return base.OnSelectPlace(cardId, player, location, available); } + + protected override bool DefaultSetForDiabellze() + { + if (base.DefaultSetForDiabellze()) + { + AI.SelectPlace(SelectSTPlace(Card, true)); + return true; + } + return false; + } } -} +} \ No newline at end of file diff --git a/Game/AI/Decks/ApophisExecutor.cs b/Game/AI/Decks/ApophisExecutor.cs new file mode 100644 index 00000000..546cf59a --- /dev/null +++ b/Game/AI/Decks/ApophisExecutor.cs @@ -0,0 +1,3076 @@ +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +using System; +using System.Linq; +using System.Diagnostics; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Apophis", "AI_Apophis")] + public class ApophisExecutor : DefaultExecutor + { + public class CardId + { + public const int LabradoriteDragon = 62514770; + public const int AnubisTheLastJudge = 60411677; + public const int PrimiteDragonEtherBeryl = 63198739; + public const int TheManWithTheMark = 97522863; + // PotOfExtravagance = 49238328; + public const int Terraforming = 73628505; + public const int PrimiteDrillbeam = 29095457; + public const int PrimiteLordlyLode = 56506740; + public const int TreasuresOfTheKings = 69299029; + public const int DominusSpark = 6325660; + // InfiniteImpermanence = 10045474; + public const int DominusImpulse = 40366667; + public const int SongsOfTheDominators = 58053438; + public const int DominusPurge = 97045737; + public const int ApophisTheSwampDeity = 85888377; + public const int ApophisTheSerpent = 95561146; + public const int VerdictOfAnubis = 59576447; + public const int SolemnReport = 78114463; + public const int DivineSerpentApophis = 97800311; + public const int SwordsoulSupremeSovereignChengying = 96633955; + public const int BaronneDeFleur = 84815190; + public const int SuperdreadnoughtRailCannonJuggernautLiebe = 26096328; + public const int SuperdreadnoughtRailCannonFlyingLauncher = 38354018; + public const int EvilswarmExcitonKnight = 46772449; + public const int SPLittleKnight = 46772449; + public const int SilhouhatteRabbit = 1528054; + public const int LinkSpider = 98978921; + } + + public ApophisExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + // counter + AddExecutor(ExecutorType.Activate, CardId.SolemnReport, SolemnReportBanishActivate); + AddExecutor(ExecutorType.Activate, CardId.ApophisTheSwampDeity, ApophisTheSwampDeityActivate); + AddExecutor(ExecutorType.Activate, CardId.SongsOfTheDominators, SongsOfTheDominatorsActivateFirst); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, CardId.SongsOfTheDominators, DominusNegateTrapActivate); + AddExecutor(ExecutorType.Activate, CardId.DominusPurge, DominusNegateTrapActivate); + AddExecutor(ExecutorType.Activate, CardId.DominusImpulse, DominusNegateTrapActivate); + AddExecutor(ExecutorType.Activate, CardId.BaronneDeFleur, BaronneDeFleurNegateEffect); + AddExecutor(ExecutorType.Activate, CardId.PrimiteDrillbeam, PrimiteDrillbeamActivate); + AddExecutor(ExecutorType.Activate, CardId.VerdictOfAnubis, SpellNegateActivate); + AddExecutor(ExecutorType.Activate, CardId.SolemnReport, SpellNegateActivate); + AddExecutor(ExecutorType.Activate, CardId.SPLittleKnight, SPLittleKnightActivate); + AddExecutor(ExecutorType.Activate, CardId.DominusSpark, DominusSparkActivate); + + // first in main phase + AddExecutor(ExecutorType.Activate, _CardId.PotOfExtravagance, PotOfExtravaganceActivate); + AddExecutor(ExecutorType.Repos, MonstetReposForImportantMonsters); + + // startup effect + AddExecutor(ExecutorType.Activate, CardId.BaronneDeFleur, BaronneDeFleurActivate); + AddExecutor(ExecutorType.Activate, CardId.SuperdreadnoughtRailCannonJuggernautLiebe, SuperdreadnoughtRailCannonJuggernautLiebeActivate); + AddExecutor(ExecutorType.Activate, CardId.SuperdreadnoughtRailCannonFlyingLauncher, SuperdreadnoughtRailCannonFlyingLauncherActivate); + AddExecutor(ExecutorType.Activate, CardId.EvilswarmExcitonKnight, DefaultEvilswarmExcitonKnightEffect); + AddExecutor(ExecutorType.SpellSet, SpellSetFirst); + AddExecutor(ExecutorType.Activate, CardId.SilhouhatteRabbit, SilhouhatteRabbitActivate); + AddExecutor(ExecutorType.Activate, CardId.DivineSerpentApophis, DivineSerpentApophisActivate); + AddExecutor(ExecutorType.Activate, CardId.SwordsoulSupremeSovereignChengying, SwordsoulSupremeSovereignChengyingActivate); + AddExecutor(ExecutorType.Activate, CardId.TheManWithTheMark, TheManWithTheMarkActivate); + + // spsummon + AddExecutor(ExecutorType.SpSummon, CardId.EvilswarmExcitonKnight, EvilswarmExcitonKnightSummon); + AddExecutor(ExecutorType.SpSummon, CardId.AnubisTheLastJudge, AnubisTheLastJudgeSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SuperdreadnoughtRailCannonJuggernautLiebe, SuperdreadnoughtRailCannonJuggernautLiebeSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SuperdreadnoughtRailCannonFlyingLauncher, SuperdreadnoughtRailCannonFlyingLauncherSpSummon); + AddExecutor(ExecutorType.SpSummon, Level10SynchroSummon); + AddExecutor(ExecutorType.SpSummon, CardId.DivineSerpentApophis, DivineSerpentApophisSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.LinkSpider, LinkSpiderSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SPLittleKnight, SPLittleKnightSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SilhouhatteRabbit, SilhouhatteRabbitSummon); + AddExecutor(ExecutorType.Activate, CardId.ApophisTheSerpent, ApophisTheSerpentActivate); + + // search + AddExecutor(ExecutorType.Activate, CardId.Terraforming, TerraformingActivate); + AddExecutor(ExecutorType.Activate, CardId.TreasuresOfTheKings, TreasuresOfTheKingsActivate); + AddExecutor(ExecutorType.Activate, CardId.PrimiteLordlyLode, PrimiteLordlyLodeActivate); + AddExecutor(ExecutorType.Summon, Level4MonsterSummon); + AddExecutor(ExecutorType.Activate, CardId.AnubisTheLastJudge, AnubisTheLastJudgeActivate); + AddExecutor(ExecutorType.Activate, CardId.PrimiteDragonEtherBeryl, PrimiteDragonEtherBerylActivate); + AddExecutor(ExecutorType.Activate, CardId.PrimiteLordlyLode, PrimiteLordlyLodeSpSummon); + AddExecutor(ExecutorType.Activate, CardId.ApophisTheSwampDeity, ApophisTheSwampDeityActivateForAttack); + + // next turn prepare + AddExecutor(ExecutorType.Repos, MonsterRepos); + AddExecutor(ExecutorType.SpellSet, SpellSet); + } + + List NotToNegateIdList = new List + { + 58699500, 20343502, 25451383, 19403423 + }; + List solemnReportBanishIdList = new List { + _CardId.MysticalSpaceTyphoon, 63166095, 9726840, 5380979, 92714517, 6153210, 32548318, 30271097, 45171524, 81560239 + }; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.TheManWithTheMark, CardId.PrimiteLordlyLode, CardId.TreasuresOfTheKings, CardId.DominusSpark, + _CardId.InfiniteImpermanence, CardId.SongsOfTheDominators, CardId.DominusPurge, CardId.ApophisTheSerpent}}, + {2, new List { CardId.AnubisTheLastJudge, CardId.PrimiteDragonEtherBeryl, _CardId.PotOfExtravagance, CardId.DominusImpulse, + CardId.ApophisTheSwampDeity, CardId.SolemnReport}}, + {1, new List { CardId.LabradoriteDragon, CardId.Terraforming, CardId.PrimiteDrillbeam, CardId.VerdictOfAnubis }} + }; + const int hintTimingMainEnd = 0x4; + const int hintToHand = 0x200000; + + int maxSummonCount = 1; + int summonCount = 1; + bool enemyActivateMonsterEffectFromHandGrave = false; + int dimensionShifterCount = 0; + int songsOfTheDominatorsResolvedCount = 0; + bool activatingLodeSpSummonEffect = false; + bool lodeSpSummonEffectResolved = false; + bool songsOfTheDominatorsActivatedFromHand = false; + List currentNegateCardList = new List(); + List activatedCardIdList = new List(); + List enemyPlaceThisTurn = new List(); + List summonThisTurn = new List(); + List placedThisTurn = new List(); + List activatedDivineSerpent1stList = new List(); + List activatedDivineSerpent2ndList = new List(); + + int anubisTheLastJudgeSpSummoningStep = 0; + int SPLittleKnightRemoveStep = 0; + int currentSummoningCount = 0; + + /// + /// Shuffle List and return a random-order card list + /// + public List ShuffleList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + T tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } + + public bool CheckCanBeTargeted(ClientCard card, bool canBeTarget, CardType selfType) + { + if (card == null) return true; + if (canBeTarget) + { + if (card.IsShouldNotBeTarget()) return false; + if (((int)selfType & (int)CardType.Monster) > 0 && card.IsShouldNotBeMonsterTarget()) return false; + if (((int)selfType & (int)CardType.Spell) > 0 && card.IsShouldNotBeSpellTrapTarget()) return false; + if (((int)selfType & (int)CardType.Trap) > 0 && card.IsShouldNotBeSpellTrapTarget() + && !card.IsDisabled()) return false; + } + return true; + } + + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + + public int CheckRemainInDeck(params int[] ids) + { + int sum = 0; + foreach (int id in ids) + { + sum += CheckRemainInDeck(id); + } + return sum; + } + + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated(bool disablecheck = true, bool toFieldCheck = false, CardType type = 0, bool ignore41 = false) + { + bool isMonster = type == 0 && Card.IsMonster(); + isMonster |= (type & CardType.Monster) != 0; + bool isSpellOrTrap = type == 0 && (Card.IsSpell() || Card.IsTrap()); + isSpellOrTrap |= (type & (CardType.Spell | CardType.Trap)) != 0; + bool isCounter = (type & CardType.Counter) != 0; + if (isSpellOrTrap && toFieldCheck) + { + if (CheckSpellWillBeNegate(isCounter)) return true; + if (DefaultCheckWhetherSpellActivateWillBeNegated(Card)) return true; + } + if (DefaultCheckWhetherCardIsNegated(Card)) return true; + if (isMonster && (toFieldCheck || Card.Location == CardLocation.MonsterZone)) + { + if ((toFieldCheck && ((type & CardType.Link) != 0)) || Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card, ignore41)) || Bot.MonsterZone.Any(card => CheckNumber41(card, ignore41))) return true; + } + if (Enemy.HasInSpellZone(_CardId.SkillDrain, true, true)) return true; + } + if (disablecheck) return (Card.Location == CardLocation.MonsterZone || Card.Location == CardLocation.SpellZone) && Card.IsDisabled() && Card.IsFaceup(); + return false; + } + + public bool CheckNumber41(ClientCard card, bool ignoreSelf41 = false) + { + return card != null && card.IsFaceup() && card.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled() + && (!ignoreSelf41 || card.Controller == 0); + } + + /// + /// Whether spell or trap will be negate. If so, return true. + /// + /// is counter trap + /// check target + /// + public bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + // target default set + if (target == null) target = Card; + // won't negate if not on field + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + // negate judge + if (Enemy.HasInMonstersZone(_CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(_CardId.NaturiaBeast, true)) return true; + if (Enemy.HasInSpellZone(_CardId.ImperialOrder, true) || Bot.HasInSpellZone(_CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(_CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(_CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap() && (Enemy.HasInSpellZone(_CardId.RoyalDecreel, true) || Bot.HasInSpellZone(_CardId.RoyalDecreel, true))) return true; + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceNegatedColumns.Contains(selfSeq)) return true; + } + // how to get here? + return false; + } + + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + return CheckCardShouldNegate(lastcard); + } + + public bool CheckCardShouldNegate(ClientCard card) + { + if (card == null) return false; + if (card.IsMonster() && card.HasSetcode(_Setcode.TimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (NotToNegateIdList.Contains(card.Id)) return false; + if (card.HasSetcode(_Setcode.Danger) && card.Location == CardLocation.Hand) return false; + if (card.IsMonster() && card.Location == CardLocation.MonsterZone && card.HasPosition(CardPosition.Defence)) + { + if (Enemy.MonsterZone.Any(c => CheckNumber41(c)) || Bot.MonsterZone.Any(c => CheckNumber41(c))) return false; + } + if (DefaultCheckWhetherCardIsNegated(card)) return false; + if (card.Location == CardLocation.SpellZone) + { + int sequence = card.Sequence; + if (card.Controller == 1) sequence = 4 - sequence; + if (infiniteImpermanenceNegatedColumns.Contains(sequence)) return false; + } + if (card.IsCode(_CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, _CardId.MulcharmyNyalus, _CardId.MaxxC)) return false; + if (card.IsDisabled()) return false; + + return true; + } + + public bool CheckCardShouldNegate(ChainInfo chainInfo) + { + if (chainInfo == null) return false; + ClientCard card = chainInfo.RelatedCard; + + if (card == null) return false; + if (card.IsMonster() && card.HasSetcode(_Setcode.TimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (NotToNegateIdList.Contains(card.Id)) return false; + if (card.HasSetcode(_Setcode.Danger) && card.Location == CardLocation.Hand) return false; + if (card.IsMonster() && chainInfo.HasLocation(CardLocation.MonsterZone) && chainInfo.HasPosition(CardPosition.Defence)) + { + if (Enemy.MonsterZone.Any(c => CheckNumber41(c)) || Bot.MonsterZone.Any(c => CheckNumber41(c))) return false; + } + if (DefaultCheckWhetherCardIsNegated(card)) return false; + if (Duel.Player == 1 && card.IsCode(_CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, _CardId.MulcharmyNyalus)) return false; + if (card.IsDisabled()) return false; + + return true; + } + + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && (Duel.Player == 0 || Bot.GetMonsterCount() > 0)) return true; + return false; + } + + public bool CheckShouldNoMoreSpSummon() + { + if (CheckAtAdvantage() && enemyResolvedEffectIdList.Contains(_CardId.MaxxC) && DefaultCheckWhetherEnemyCanDraw() && (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2)) + { + return true; + } + return false; + } + + public bool CheckShouldNoMoreSpSummon(CardLocation loc) + { + if (CheckShouldNoMoreSpSummon()) return true; + if (!DefaultCheckWhetherEnemyCanDraw() || (Duel.Turn > 1 && Duel.Phase < DuelPhase.Main2)) return false; + if (enemyResolvedEffectIdList.Contains(_CardId.MulcharmyPurulia) && (loc & CardLocation.Hand) != 0) return true; + if (enemyResolvedEffectIdList.Contains(_CardId.MulcharmyFuwalos) && (loc & (CardLocation.Deck | CardLocation.Extra)) != 0) return true; + if (enemyResolvedEffectIdList.Contains(_CardId.MulcharmyNyalus) && (loc & (CardLocation.Grave | CardLocation.Removed)) != 0) return true; + + return false; + } + + public bool CheckWhetherCanSummon() + { + return Duel.Player == 0 && Duel.Phase < DuelPhase.End && summonCount > 0; + } + + /// + /// Check whether cards will be removed. If so, do not send cards to grave. + /// + public bool CheckWhetherWillbeRemoved() + { + if (dimensionShifterCount > 0) return true; + List checkIdList = new List { _CardId.BanisheroftheRadiance, _CardId.BanisheroftheLight, _CardId.MacroCosmos, _CardId.DimensionalFissure, + _CardId.KashtiraAriseHeart, _CardId.MaskedHERODarkLaw }; + foreach (int cardid in checkIdList) + { + List fields = new List { Bot, Enemy }; + foreach (ClientField cf in fields) + { + if (cf.HasInMonstersZone(cardid, true, false, true) || cf.HasInSpellZone(cardid, true, true)) + { + return true; + } + } + } + return false; + } + + public bool CheckWhetherCanActivateMonsterEffect(CardAttribute attribute) + { + if (Bot.HintDescriptions.Contains(Util.GetStringId(CardId.DominusSpark, 3)) + && (attribute & (CardAttribute.Earth | CardAttribute.Water | CardAttribute.Fire | CardAttribute.Wind)) != 0) + { + return false; + } + if (Bot.HintDescriptions.Contains(Util.GetStringId(CardId.DominusImpulse, 2)) + && (attribute & (CardAttribute.Light | CardAttribute.Earth | CardAttribute.Wind)) != 0) + { + return false; + } + if (Bot.HintDescriptions.Contains(Util.GetStringId(CardId.DominusPurge, 2)) + && (attribute & (CardAttribute.Dark | CardAttribute.Water | CardAttribute.Fire)) != 0) + { + return false; + } + return true; + } + + public int CompareUsableAttack(ClientCard cardA, ClientCard cardB) + { + if (cardA == null && cardB == null) + return 0; + if (cardA == null) + return -1; + if (cardB == null) + return 1; + int powerA = (cardA.IsDefense() && summonThisTurn.Contains(cardA)) ? 0 : cardA.Attack; + int powerB = (cardB.IsDefense() && summonThisTurn.Contains(cardB)) ? 0 : cardB.Attack; + if (powerA < powerB) + return -1; + if (powerA == powerB) + return CardContainer.CompareCardLevel(cardA, cardB); + return 1; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = true, CardType selfType = 0) + { + ClientCard floodagateCard = Enemy.GetMonsters().Where(c => c?.Data != null && (!ignoreCurrentDestroy || !currentNegateCardList.Contains(c)) + && c.IsFloodgate() && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (floodagateCard != null) return floodagateCard; + + ClientCard dangerCard = Enemy.MonsterZone.Where(c => c?.Data != null && (!ignoreCurrentDestroy || !currentNegateCardList.Contains(c)) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (dangerCard != null) return dangerCard; + + ClientCard invincibleCard = Enemy.MonsterZone.Where(c => c?.Data != null && (!ignoreCurrentDestroy || !currentNegateCardList.Contains(c)) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (invincibleCard != null) return invincibleCard; + + ClientCard equippedCard = Enemy.MonsterZone.Where(c => c?.Data != null && (!ignoreCurrentDestroy || !currentNegateCardList.Contains(c)) + && c.EquipCards.Count > 0 && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (equippedCard != null) return equippedCard; + + ClientCard enemyExtraMonster = Enemy.MonsterZone.Where(c => c != null && (!ignoreCurrentDestroy || !currentNegateCardList.Contains(c)) + && (c.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) || (c.HasType(CardType.Link) && c.LinkCount >= 2)) + && CheckCanBeTargeted(c, canBeTarget, selfType) && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (enemyExtraMonster != null) return enemyExtraMonster; + + ClientCard activatingAlbaz = Enemy.MonsterZone.FirstOrDefault(c => c != null && c.IsCode(68468459) && !c.IsDisabled() + && !currentNegateCardList.Contains(c) && Duel.CurrentChain.Contains(c)); + if (activatingAlbaz != null) return activatingAlbaz; + + if (attack >= 0) + { + if (attack == 0) + attack = GetBotBestPower(); + ClientCard betterCard = Enemy.MonsterZone.Where(card => card != null + && card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && CheckCanBeTargeted(card, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentNegateCardList.Contains(card))).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (betterCard != null) return betterCard; + } + return null; + } + + public bool CheckShouldNotIgnore(ClientCard cards, bool ignore = false) + { + return !ignore || (!currentNegateCardList.Contains(cards) && !currentNegateCardList.Contains(cards)); + } + + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreSpells = false, CardType selfType = 0) + { + List resultList = new List(); + + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !currentNegateCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count > 0) resultList.AddRange(floodagateList); + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentNegateCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).ToList(); + if (problemEnemySpellList.Count > 0) resultList.AddRange(ShuffleList(problemEnemySpellList)); + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentNegateCardList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) resultList.AddRange(dangerList); + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentNegateCardList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count > 0) resultList.AddRange(invincibleList); + + List enemyMonsters = Enemy.GetMonsters().Where(c => !currentNegateCardList.Contains(c)).OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count > 0) + { + foreach (ClientCard target in enemyMonsters) + { + if ((target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && !resultList.Contains(target) && CheckCanBeTargeted(target, canBeTarget, selfType) + ) + { + resultList.Add(target); + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !currentNegateCardList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) && CheckCanBeTargeted(c, canBeTarget, selfType)).ToList(); + if (spells.Count > 0 && !ignoreSpells) resultList.AddRange(ShuffleList(spells)); + + return resultList; + } + + public List GetNormalEnemyTargetList(bool canBeTarget = true, bool ignoreCurrentDestroy = true, CardType selfType = 0, bool forNegate = false) + { + List targetList = GetProblematicEnemyCardList(canBeTarget, selfType: selfType); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && (!ignoreCurrentDestroy || !currentNegateCardList.Contains(card)) + && (!forNegate || (!card.IsDisabled() && card.HasType(CardType.Effect))) + ).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentNegateCardList.Contains(card)) && enemyPlaceThisTurn.Contains(card) && card.IsFacedown()).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentNegateCardList.Contains(card)) && !enemyPlaceThisTurn.Contains(card) && card.IsFacedown()).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() + && (!ignoreCurrentDestroy || !currentNegateCardList.Contains(card)) + && (!forNegate || (!card.IsDisabled() && card.HasType(CardType.Effect) && card.IsFaceup())) + ).ToList())); + + return targetList; + } + + public List GetNormalEnemySpellTargetList(bool canBeTarget = true, bool ignoreCurrentDestroy = true, CardType selfType = 0, bool forNegate = false) + { + List targetList = GetNormalEnemyTargetList(canBeTarget, ignoreCurrentDestroy, selfType, forNegate); + return targetList.Where(card => card.HasType(CardType.Spell | CardType.Trap) || card.Location == CardLocation.SpellZone).ToList(); + } + + public List GetMonsterListForTargetNegate(bool canBeTarget = false, CardType selfType = 0) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; + } + + // negate before used + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && CheckCanBeTargeted(card, canBeTarget, selfType) + && !currentNegateCardList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + // negate monster effect on the field + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && CheckCanBeTargeted(chainingCard, canBeTarget, selfType) && !currentNegateCardList.Contains(chainingCard)) + { + if (chainingCard.HasPosition(CardPosition.Defence)) + { + bool have41 = Bot.MonsterZone.Any(c => CheckNumber41(c)) | Enemy.MonsterZone.Any(c => CheckNumber41(c)); + } + resultList.Add(chainingCard); + } + } + + return resultList; + } + + public int GetSpecialSummonDrawCount(CardLocation loc) + { + int res = 0; + if (!DefaultCheckWhetherEnemyCanDraw()) + { + return 0; + } + if (enemyResolvedEffectIdList.Contains(_CardId.MaxxC)) + { + res++; + } + + if ((loc & CardLocation.Hand) != 0) + { + res += enemyResolvedEffectIdList.Count(id => id == _CardId.MulcharmyPurulia); + } + if ((loc & (CardLocation.Deck | CardLocation.Extra)) != 0) + { + res += enemyResolvedEffectIdList.Count(id => id == _CardId.MulcharmyFuwalos); + } + if ((loc & (CardLocation.Grave | CardLocation.Removed)) != 0) + { + res += enemyResolvedEffectIdList.Count(id => id == _CardId.MulcharmyNyalus); + } + + return res; + } + + public int GetBotBestPower(bool onlyATK = false) + { + return Bot.MonsterZone.GetMonsters() + .Where(card => !onlyATK || (!summonThisTurn.Contains(card) && Duel.Player == 0) || card.IsAttack()) + .Max(card => (int?)card.GetDefensePower()) ?? -1; + } + + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + ChainInfo currentChain = Duel.GetCurrentSolvingChainInfo(); + if (currentChain == null) { + if (anubisTheLastJudgeSpSummoningStep > 0) { + List cardIdList = new List { + CardId.VerdictOfAnubis, CardId.ApophisTheSwampDeity, CardId.SongsOfTheDominators, CardId.DominusSpark, CardId.DominusImpulse, + CardId.ApophisTheSerpent, CardId.DominusPurge, _CardId.InfiniteImpermanence, CardId.SolemnReport }; + foreach (int cardId in cardIdList) { + ClientCard card = cards.FirstOrDefault(c => c.IsCode(cardId)); + if (card != null) { + if (anubisTheLastJudgeSpSummoningStep == 1) + { + anubisTheLastJudgeSpSummoningStep = 2; + } else + { + anubisTheLastJudgeSpSummoningStep = 0; + } + return Util.CheckSelectCount(new List { card }, cards, min, max); + } + } + } + + // for activating target + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && lastChainCard.Controller == 0) + { + switch (lastChainCard.Id) + { + case CardId.AnubisTheLastJudge: + { + List targetList = GetNormalEnemyTargetList(canBeTarget: true, ignoreCurrentDestroy: true, selfType: CardType.Monster, forNegate: false); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + // destroy set this turn + foreach (ClientCard target in cards) + { + if (enemyPlaceThisTurn.Contains(target)) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + } + case CardId.PrimiteDrillbeam: + { + // negate cards on chain + foreach (ClientCard card in Duel.CurrentChain) { + if (card.Controller == 1 && card.IsOnField() && card.IsFaceup() && !card.IsDisabled() && !currentNegateCardList.Contains(card) + && CheckCanBeTargeted(card, true, CardType.Spell) && CheckCardShouldNegate(card) && cards.Contains(card)) { + currentNegateCardList.Add(card); + return Util.CheckSelectCount(new List { card }, cards, min, max); + } + } + // remove problematic enemy cards + List targetList = GetNormalEnemyTargetList(canBeTarget: true, ignoreCurrentDestroy: true, selfType: CardType.Monster, forNegate: false); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + // select enemy cards + List enemyCards = ShuffleList(cards.Where(c => c.Controller == 1).ToList()); + foreach (ClientCard target in enemyCards) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + } + case CardId.DominusSpark: + { + List targetList = GetNormalEnemyTargetList(ignoreCurrentDestroy: false); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + } + case _CardId.InfiniteImpermanence: + { + int sequence = lastChainCard.Sequence; + List targetList = GetMonsterListForTargetNegate(true, CardType.Trap); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + if (sequence >= 0) + { + ClientCard spell = Enemy.SpellZone[sequence]; + if (spell != null && spell.IsFaceup()) + { + currentNegateCardList.Add(spell); + } + } + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + targetList = GetProblematicEnemyCardList(canBeTarget: true, ignoreSpells: false, selfType: CardType.Trap); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + if (sequence >= 0) + { + ClientCard spell = Enemy.SpellZone[sequence]; + if (spell != null && spell.IsFaceup()) + { + currentNegateCardList.Add(spell); + } + } + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + } + case CardId.DivineSerpentApophis: + { + if (hint == HintMsg.Destroy) + { + List targetList = GetNormalEnemyTargetList(); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + if (hint == HintMsg.Set) + { + int targetId = CardId.ApophisTheSwampDeity; + // if no other continuous trap can be found, then use ApophisTheSerpent + bool hasOtherContinuousTrap = false; + if (Bot.GetSpellCountWithoutField() <= 3 && Bot.HasInGraveyard(CardId.ApophisTheSerpent)) + { + hasOtherContinuousTrap = true; + } + if (Bot.GetSpells().Any(c => c.HasType(CardType.Continuous)) || Bot.GetMonsters().Any(c => c.HasType(CardType.Continuous))) + { + hasOtherContinuousTrap = true; + } + if (!hasOtherContinuousTrap) + { + targetId = CardId.ApophisTheSerpent; + } + ClientCard target = cards.FirstOrDefault(c => c.IsCode(targetId)); + if (target != null) + { + currentNegateCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + // select remaining cards + return Util.CheckSelectCount(cards, cards, min, max); + } + break; + } + case CardId.BaronneDeFleur: + { + if (hint == HintMsg.Destroy) + { + List targetList = GetNormalEnemyTargetList(); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + } + case CardId.SuperdreadnoughtRailCannonFlyingLauncher: + { + List targetList = GetNormalEnemySpellTargetList(true, false, CardType.Monster); + if (hint == HintMsg.RemoveXyz) + { + return Util.CheckSelectCount(cards, cards, min, Math.Min(targetList.Count, max)); + } + if (hint == HintMsg.Destroy) + { + List destroyList = new List(); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + destroyList.Add(target); + if (destroyList.Count >= max) + { + currentNegateCardList.AddRange(destroyList); + return Util.CheckSelectCount(destroyList, cards, min, Math.Min(targetList.Count, max)); + } + } + } + } + break; + } + case CardId.SPLittleKnight: + { + if (Duel.CurrentChainInfo.Count > 0) + { + ChainInfo lastChain = Duel.CurrentChainInfo[Duel.CurrentChainInfo.Count - 1]; + if (lastChain.ActivateDescription == -1 || lastChain.ActivateDescription == Util.GetStringId(CardId.SPLittleKnight, 0)) + { + // remove enemy cards + List problemCardList = GetProblematicEnemyCardList(true, selfType: CardType.Monster); + problemCardList.AddRange(GetNormalEnemyTargetList(true, true, CardType.Monster)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => card.HasType(CardType.Monster)).OrderByDescending(card => card.Attack)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => !card.HasType(CardType.Monster))); + foreach (ClientCard target in problemCardList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } else if (lastChain.ActivateDescription == Util.GetStringId(CardId.SPLittleKnight, 1)) + { + switch (SPLittleKnightRemoveStep) + { + case 1: + // remove target + foreach (ClientCard target in Bot.GetMonsters()) + { + if (target.HasType(CardType.Continuous)) + { + continue; + } + if (Duel.ChainTargets.Contains(target) && cards.Contains(target)) + { + SPLittleKnightRemoveStep = 2; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + // remove weak monster + if (Duel.Player == 1) + { + foreach (ClientCard card in Bot.GetMonsters().Where(c => c.IsAttack() && !c.HasType(CardType.Continuous) && !c.IsCode(new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity })).OrderBy(c => c.Attack)) + { + if (Util.IsOneEnemyBetterThanValue(card.Attack, true) && cards.Contains(card)) + { + SPLittleKnightRemoveStep = 2; + return Util.CheckSelectCount(new List { card }, cards, min, max); + } + } + } + break; + case 2: + { + // select problematic enemy monster + if (Enemy.GetMonsterCount() > 0) + { + List problemList = GetProblematicEnemyCardList(true, true, CardType.Monster); + if (problemList.Count() > 0) + { + foreach (ClientCard target in problemList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + SPLittleKnightRemoveStep = 0; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + } + // remove bot's target + foreach (ClientCard target in Duel.ChainTargets) + { + if (target.IsCode(new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity })) + { + continue; + } + if (cards.Contains(target)) + { + SPLittleKnightRemoveStep = 0; + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + // remove bot's monster + if (Enemy.GetMonsterCount() == 0) + { + List otherOwn = Bot.GetMonsters().Where(c => !c.HasType(CardType.Continuous)).ToList(); + otherOwn.Sort(CompareUsableAttack); + foreach (ClientCard c in otherOwn) + { + if (cards.Contains(c)) + { + SPLittleKnightRemoveStep = 0; + return Util.CheckSelectCount(new List { c }, cards, min, max); + } + } + } + } + break; + default: + SPLittleKnightRemoveStep = 0; + break; + } + } + } + break; + } + case CardId.SilhouhatteRabbit: + { + if (hint == HintMsg.Destroy) + { + List targetList = GetNormalEnemySpellTargetList(true, false, CardType.Monster, false); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + currentNegateCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + } + default: + break; + } + } + } + + if (currentChain != null) + { + if (currentChain.ActivateController == 0) + { + // for solving chain + switch (currentChain.ActivateId) + { + case CardId.PrimiteDragonEtherBeryl: + if (hint == HintMsg.Set) + { + int targetId = CardId.PrimiteLordlyLode; + if (activatedCardIdList.Contains(CardId.PrimiteLordlyLode) || !DefaultCheckWhetherBotCanSearch() || Bot.HasInSpellZone(CardId.PrimiteLordlyLode)) + { + targetId = CardId.PrimiteDrillbeam; + } + ClientCard target = cards.FirstOrDefault(c => c.IsCode(targetId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + case CardId.TheManWithTheMark: + if (hint == HintMsg.OperateCard) + { + int targetId = CardId.TreasuresOfTheKings; + if (activatedCardIdList.Contains(CardId.TreasuresOfTheKings) || Bot.HasInHandOrInSpellZone(CardId.TreasuresOfTheKings)) + { + targetId = CardId.VerdictOfAnubis; + } + ClientCard target = cards.FirstOrDefault(c => c.IsCode(targetId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + case CardId.TreasuresOfTheKings: + { + List targetIdList = new List(); + if (hint == HintMsg.Set) + { + targetIdList.AddRange(new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity }); + } else if (hint == HintMsg.AddToHand) { + if (songsOfTheDominatorsResolvedCount > 0) + { + targetIdList.AddRange(new List { CardId.TheManWithTheMark, CardId.AnubisTheLastJudge }); + } else + { + targetIdList.AddRange(new List { CardId.AnubisTheLastJudge, CardId.TheManWithTheMark }); + } + } + foreach (int targetId in targetIdList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(targetId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + } + case CardId.PrimiteLordlyLode: + { + if (hint == HintMsg.AddToHand) + { + List targetIdList = new List { CardId.PrimiteDragonEtherBeryl, CardId.PrimiteDrillbeam }; + if (summonCount == 0 || !CheckWhetherCanActivateMonsterEffect(CardAttribute.Earth)) + { + // whether need to search drillbeam + bool canTriggerPrimiteBeam = Bot.Hand.Any(c => c.IsCode(CardId.PrimiteDragonEtherBeryl, CardId.PrimiteLordlyLode, CardId.LabradoriteDragon)); + canTriggerPrimiteBeam |= Bot.HasInMonstersZone(new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity, CardId.LabradoriteDragon }, faceUp: true); + canTriggerPrimiteBeam |= Bot.GetSpells().Any(c => c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity) && (c.IsFacedown() || Duel.CurrentChain.Contains(c))); + if (canTriggerPrimiteBeam) + { + targetIdList.Insert(0, CardId.PrimiteDrillbeam); + } + } + foreach (int targetId in targetIdList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(targetId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + break; + } + case CardId.SongsOfTheDominators: + { + List checkIdList = new List { CardId.DominusPurge, CardId.DominusImpulse, CardId.DominusSpark }; + // 1. Prefer a card NOT in activatedCardIdList and not in hand / spell&trap of bot + foreach (int checkId in checkIdList) + { + if (!activatedCardIdList.Contains(checkId) && !Bot.HasInHandOrInSpellZone(checkId) && !Duel.CurrentChain.Any(c => c != null && c.Controller == 0 && c.IsCode(checkId))) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(checkId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + // 2. Next, prefer a card not in hand/spell&trap + foreach (int checkId in checkIdList) + { + if (!Bot.HasInHandOrInSpellZone(checkId)) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(checkId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + // 3. Finally, pick by id order if any exists + foreach (int checkId in checkIdList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(checkId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + } + case CardId.ApophisTheSwampDeity: + { + List negateCardList = new List(); + // select cards to negate + for (int i = Duel.CurrentChain.Count - 1; i >= 0; i--) + { + ClientCard card = Duel.CurrentChain[i]; + if (card != null && card.IsFaceup() && cards.Contains(card)) + { + negateCardList.Add(card); + } + } + // negate other face-up cards + foreach (ClientCard card in cards) + { + if (card != null && card.IsFaceup() && !negateCardList.Contains(card)) + { + negateCardList.Add(card); + } + } + return Util.CheckSelectCount(negateCardList, cards, min, max); + } + case CardId.SwordsoulSupremeSovereignChengying: + { + List fieldTargetList = GetNormalEnemyTargetList(false); + foreach (ClientCard target in fieldTargetList) + { + if (cards.Contains(target)) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + // banish fron grave + List graveTargetList = Duel.CurrentChain.Where(c => c != null && c.Controller == 1 && c.Location == CardLocation.Grave).ToList(); + graveTargetList.AddRange(Duel.ChainTargets.Where(c => c != null && c.Controller == 1 && c.Location == CardLocation.Grave).ToList()); + graveTargetList.AddRange(Enemy.Graveyard.Where(c => c.IsMonster()).OrderByDescending(c => c.Attack).ToList()); + return Util.CheckSelectCount(graveTargetList, cards, min, max); + } + case CardId.SilhouhatteRabbit: + { + List targetIdList = new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity }; + foreach (int targetId in targetIdList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(targetId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + break; + } + default: + break; + } + } + if (currentChain.ActivateController == 1) + { + switch (currentChain.ActivateId) + { + case _CardId.EvenlyMatched: + { + Logger.DebugWriteLine("=== Evenly Matched activated."); + List banishList = new List(); + List botMonsters = Bot.GetMonsters().Where(card => !card.HasType(CardType.Token)).ToList(); + // monster + List faceDownMonsters = botMonsters.Where(card => card.IsFacedown()).ToList(); + banishList.AddRange(faceDownMonsters); + List dumpMainMonsterList = botMonsters.Where(card => !banishList.Contains(card) + && CheckRemainInDeck(card.Id) > 0).ToList(); + dumpMainMonsterList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(dumpMainMonsterList); + // spells + List faceUpSpells = Bot.GetSpells().Where(c => c.IsFaceup()).ToList(); + banishList.AddRange(ShuffleList(faceUpSpells)); + // other monster + List otherMonsters = botMonsters.Where(card => !banishList.Contains(card)).ToList(); + otherMonsters.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(otherMonsters); + List faceDownSpells = Bot.GetSpells().Where(c => c.IsFacedown()).ToList(); + banishList.AddRange(ShuffleList(faceDownSpells)); + return Util.CheckSelectCount(banishList, cards, min, max); + } + default: + break; + } + } + } + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + public override bool OnSelectHand() + { + return true; + } + + public override int OnSelectOption(IList options) + { + if (options.Contains(Util.GetStringId(_CardId.PotOfExtravagance, 1))) + { + return options.IndexOf(Util.GetStringId(_CardId.PotOfExtravagance, 1)); + } + if (options.Contains(Util.GetStringId(CardId.SolemnReport, 1)) && options.Contains(Util.GetStringId(CardId.SolemnReport, 2))) + { + int destroyOpt = options.IndexOf(Util.GetStringId(CardId.SolemnReport, 1)); + int banishOpt = options.IndexOf(Util.GetStringId(CardId.SolemnReport, 2)); + bool avoidDestroyFlag = false; + if (Duel.CurrentChain.Count >= 2) + { + ClientCard secondLastChainCard = Duel.CurrentChain[Duel.CurrentChain.Count - 2]; + if (secondLastChainCard != null) + { + // avoid destroy if bot have the same cards that can activate + switch (secondLastChainCard.Id) + { + case CardId.DominusSpark: + { + bool canActivateFlag = !activatedCardIdList.Contains(CardId.DominusSpark) && !Duel.CurrentChain.Any(c => c != null && c.Controller == 0 && c.IsCode(CardId.DominusSpark)); + if (canActivateFlag) + { + canActivateFlag &= Bot.HasInHand(CardId.DominusSpark) && enemyActivateMonsterEffectFromHandGrave + || Bot.GetSpells().Any(c => c.IsCode(CardId.DominusSpark) && !placedThisTurn.Contains(c)); + } + avoidDestroyFlag |= canActivateFlag; + break; + } + case CardId.DominusImpulse: + case CardId.DominusPurge: + { + bool canActivateFlag = !activatedCardIdList.Contains(secondLastChainCard.Id) && !Duel.CurrentChain.Any(c => c != null && c.Controller == 0 && c.IsCode(secondLastChainCard.Id)); + if (canActivateFlag) + { + canActivateFlag &= Bot.HasInHand(secondLastChainCard.Id) || Bot.GetSpells().Any(c => c.IsCode(secondLastChainCard.Id) && !placedThisTurn.Contains(c)); + } + avoidDestroyFlag |= canActivateFlag; + break; + } + case CardId.SongsOfTheDominators: + { + bool canActivateFlag = !activatedCardIdList.Contains(secondLastChainCard.Id) && !Duel.CurrentChain.Any(c => c != null && c.Controller == 0 && c.IsCode(secondLastChainCard.Id)); + if (canActivateFlag) + { + canActivateFlag &= Bot.HasInHand(secondLastChainCard.Id) && Bot.Graveyard.Count(c => c.IsMonster()) == 0 + || Bot.GetSpells().Any(c => c.IsCode(secondLastChainCard.Id) && !placedThisTurn.Contains(c)); + } + avoidDestroyFlag |= canActivateFlag; + break; + } + case _CardId.InfiniteImpermanence: + case CardId.ApophisTheSwampDeity: + avoidDestroyFlag |= Bot.GetSpells().Any(c => c.IsCode(secondLastChainCard.Id) && !placedThisTurn.Contains(c)); + break; + case CardId.ApophisTheSerpent: + avoidDestroyFlag |= Bot.GetSpells().Any(c => c.IsCode(secondLastChainCard.Id) && !placedThisTurn.Contains(c)) && !activatedCardIdList.Contains(secondLastChainCard.Id); + break; + } + + if (avoidDestroyFlag) + { + return banishOpt; + } + if (secondLastChainCard.IsCode(solemnReportBanishIdList)) + { + return banishOpt; + } + } + } + + return destroyOpt; + } + + ChainInfo currentChain = Duel.GetCurrentSolvingChainInfo(); + if (currentChain != null) + { + if (currentChain.ActivateController == 0) + { + switch (currentChain.ActivateId) + { + case CardId.TheManWithTheMark: + if (options.Contains(1190)) + { + return options.IndexOf(1190); + } + break; + default: + break; + } + } + } + + return base.OnSelectOption(options); + } + + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone) + { + List zoneIdList = ShuffleList(new List { 5, 6 }); + zoneIdList.AddRange(ShuffleList(new List { 0, 2, 4 })); + zoneIdList.AddRange(ShuffleList(new List { 1, 3 })); + foreach (int zoneId in zoneIdList) + { + int zone = (int)System.Math.Pow(2, zoneId); + if ((available & zone) != 0 && Bot.MonsterZone[zoneId] == null) + { + List infiniteImpermanenceList = Bot.GetSpells().Where(c => c.IsCode(_CardId.InfiniteImpermanence)).Select(c => c.Sequence).ToList(); + infiniteImpermanenceList.AddRange(infiniteImpermanenceNegatedColumns); + // trap monster do not summon to infinite permanence negated columns + if ((cardId == CardId.ApophisTheSerpent || cardId == CardId.ApophisTheSwampDeity) + && infiniteImpermanenceList.Contains(zoneId)) + { + continue; + } + if (cardId == CardId.ApophisTheSerpent || cardId == CardId.ApophisTheSwampDeity) + { + Logger.DebugWriteLine("Apophis select zone: " + zoneId); + Logger.DebugWriteLine("infiniteImpermanenceNegatedColumns: " + string.Join(", ", infiniteImpermanenceList)); + } + return zone; + } + } + } + ChainInfo currentChain = Duel.GetCurrentSolvingChainInfo(); + if (currentChain != null && currentChain.ActivateController == 0) + { + switch (currentChain.ActivateId) + { + case CardId.TreasuresOfTheKings: + { + List zoneIdList = ShuffleList(new List { 0, 1, 2, 3, 4 }); + foreach (int zoneId in zoneIdList) + { + int zone = (int)System.Math.Pow(2, zoneId); + if ((available & zone) != 0 && Bot.MonsterZone[zoneId] == null) + { + return zone; + } + } + break; + } + case CardId.ApophisTheSerpent: + { + List zoneIdList = ShuffleList(new List { 0, 1, 2, 3, 4 }); + foreach (int zoneId in zoneIdList) + { + int zone = (int)System.Math.Pow(2, zoneId); + if ((available & zone) != 0 && Bot.MonsterZone[zoneId] == null && !infiniteImpermanenceNegatedColumns.Contains(zoneId)) + { + return zone; + } + } + break; + } + default: + break; + } + } + return base.OnSelectPlace(cardId, player, location, available); + } + + public override bool OnSelectYesNo(long desc) + { + if (desc == Util.GetStringId(CardId.PrimiteDrillbeam, 0)) + { + return false; + } + + return base.OnSelectYesNo(desc); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2) + { + bool turnDefense = false; + if (cardData.Attack <= cardData.Defense) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } + int cardAttack = cardData.Attack; + if (cardId == CardId.SuperdreadnoughtRailCannonJuggernautLiebe && CheckWhetherCanActivateMonsterEffect(CardAttribute.Earth)) + { + cardAttack += 2000; + } + if (cardId == CardId.SwordsoulSupremeSovereignChengying) + { + int removeCount = Bot.Banished.Count() + Enemy.Banished.Count(); + cardAttack += removeCount * 200; + } + int bestBotAttack = Math.Max(GetBotBestPower(true), cardAttack); + + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + public override void OnNewTurn() + { + if (Duel.Turn <= 1) + { + dimensionShifterCount = 0; + songsOfTheDominatorsResolvedCount = 0; + // for doom bot + maxSummonCount = 1; + } + + summonCount = maxSummonCount; + enemyActivateMonsterEffectFromHandGrave = false; + anubisTheLastJudgeSpSummoningStep = 0; + SPLittleKnightRemoveStep = 0; + activatingLodeSpSummonEffect = false; + lodeSpSummonEffectResolved = false; + songsOfTheDominatorsActivatedFromHand = false; + if (dimensionShifterCount > 0) dimensionShifterCount--; + if (songsOfTheDominatorsResolvedCount > 0) songsOfTheDominatorsResolvedCount--; + currentNegateCardList.Clear(); + activatedCardIdList.Clear(); + enemyPlaceThisTurn.Clear(); + summonThisTurn.Clear(); + placedThisTurn.Clear(); + activatedDivineSerpent1stList.Clear(); + activatedDivineSerpent2ndList.Clear(); + currentSummoningCount = 0; + + base.OnNewTurn(); + } + + public override void OnChaining(int player, ClientCard card) + { + if (player == 1 && card != null && card.IsMonster() && (card.Location == CardLocation.Hand || card.Location == CardLocation.Grave)) + { + enemyActivateMonsterEffectFromHandGrave = true; + } + + base.OnChaining(player, card); + } + + public override void OnChainSolved(int chainIndex) + { + ChainInfo currentChain = Duel.GetCurrentSolvingChainInfo(); + if (currentChain != null) + { + if (currentChain.ActivateController == 0) + { + switch (currentChain.ActivateId) + { + case CardId.DominusSpark: + case CardId.DominusImpulse: + case CardId.DominusPurge: + case CardId.SongsOfTheDominators: + activatedCardIdList.Add(currentChain.ActivateId); + break; + case CardId.TreasuresOfTheKings: + if (currentChain.ActivateDescription != Util.GetStringId(CardId.TreasuresOfTheKings, 0)) + { + activatedCardIdList.Add(currentChain.ActivateId); + } + break; + } + } + if (!Duel.IsCurrentSolvingChainNegated()) + { + if (currentChain.ActivateController == 0) + { + switch (currentChain.ActivateId) + { + case CardId.PrimiteLordlyLode: + { + if (activatingLodeSpSummonEffect) + { + lodeSpSummonEffectResolved = true; + } + break; + } + case CardId.SongsOfTheDominators: + { + if (songsOfTheDominatorsActivatedFromHand) + { + songsOfTheDominatorsResolvedCount = 2; + } + break; + } + default: + break; + } + } + if (currentChain.IsActivateCode(_CardId.DimensionShifter)) + dimensionShifterCount = 2; + } + } + + base.OnChainSolved(chainIndex); + } + + public override void OnChainEnd() + { + currentSummoningCount = 0; + currentNegateCardList.Clear(); + activatingLodeSpSummonEffect = false; + songsOfTheDominatorsActivatedFromHand = false; + for (int idx = enemyPlaceThisTurn.Count - 1; idx >= 0; idx--) + { + ClientCard checkTarget = enemyPlaceThisTurn[idx]; + if (checkTarget == null || !checkTarget.IsOnField()) + { + enemyPlaceThisTurn.RemoveAt(idx); + } + } + base.OnChainEnd(); + } + + public override void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + if (card != null) + { + if (currentControler == 1 && (currentLocation == (int)CardLocation.MonsterZone || currentLocation == (int)CardLocation.SpellZone)) + { + enemyPlaceThisTurn.Add(card); + } + + if (previousControler == 0 && previousLocation == (int)CardLocation.MonsterZone && currentLocation != (int)CardLocation.MonsterZone) + { + if (summonThisTurn.Contains(card)) + summonThisTurn.Remove(card); + if (activatedDivineSerpent1stList.Contains(card)) + activatedDivineSerpent1stList.Remove(card); + if (activatedDivineSerpent2ndList.Contains(card)) + activatedDivineSerpent2ndList.Remove(card); + } + if (currentControler == 0 && currentLocation == (int)CardLocation.MonsterZone) + { + summonThisTurn.Add(card); + } + if (currentControler == 0 && previousLocation == (int)CardLocation.SpellZone && currentLocation != (int)CardLocation.SpellZone && placedThisTurn.Contains(card)) + { + placedThisTurn.Remove(card); + } + if (currentControler == 0 && currentLocation == (int)CardLocation.SpellZone) + { + ChainInfo currentChain = Duel.GetCurrentSolvingChainInfo(); + if (currentChain != null && currentChain.ActivateController == 0 && currentChain.ActivateId == CardId.ApophisTheSerpent && card.IsCode(CardId.ApophisTheSwampDeity)) + { + placedThisTurn.Remove(card); + } else + { + placedThisTurn.Add(card); + } + } + } + + base.OnMove(card, previousControler, previousLocation, currentControler, currentLocation); + } + + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + if (card.Location == CardLocation.SpellZone) + { + return; + } + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceNegatedColumns.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public bool AnubisTheLastJudgeActivate() + { + if (Card.Location == CardLocation.Hand) + { + if (CheckWhetherNegated()) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + + // select target on enemy field + List targetList = GetNormalEnemyTargetList(canBeTarget: true, ignoreCurrentDestroy: false, selfType: CardType.Monster, forNegate: false); + if (targetList.Count > 0) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool AnubisTheLastJudgeSpSummon() { + if (CheckShouldNoMoreSpSummon(CardLocation.Grave)) + { + return false; + } + + if (Duel.MainPhase.ActivableCards.Contains(Card)) + { + // whether should activate + if (!CheckWhetherNegated() || !CheckWhetherWillbeRemoved()) + { + return false; + } + } + + anubisTheLastJudgeSpSummoningStep = 1; + return true; + } + + public bool PrimiteDragonEtherBerylActivate() + { + if (Card.Location == CardLocation.Grave) + { + return !CheckWhetherNegated(); + } + // to grave + if (ActivateDescription == Util.GetStringId(CardId.PrimiteDragonEtherBeryl, 1)) + { + if (!Util.IsTurn1OrMain2()) + { + return false; + } + + // summoning Labradorite instead? + bool notCalledLode = !activatedCardIdList.Contains(CardId.PrimiteLordlyLode + 1); + if (notCalledLode) + { + // can summon? + bool enabledLodeFlag = Bot.GetSpells().Any(c => c.IsCode(CardId.PrimiteLordlyLode) && c.IsFaceup() && !c.IsDisabled()); + bool existsLode = Bot.GetSpells().Any(c => c.IsCode(CardId.PrimiteLordlyLode) && c.IsFacedown()); + existsLode = Bot.HasInHand(CardId.PrimiteLordlyLode) && Bot.GetSpellCount() < 5; + enabledLodeFlag |= existsLode && !activatedCardIdList.Contains(CardId.PrimiteLordlyLode); + + if (enabledLodeFlag) + { + return false; + } + } + + // whether can recycle next turn? + List apophisCardIdList = new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity }; + bool recycleFlag = Bot.HasInHand(apophisCardIdList) + || Bot.HasInSpellZone(apophisCardIdList) + || Bot.GetMonsters().Any(c => c.IsFaceup() && apophisCardIdList.Contains(c.Id)); + return !CheckWhetherWillbeRemoved() && (!CheckWhetherNegated() || recycleFlag); + } else + { + // search + return !CheckWhetherNegated(); + } + } + + public bool TheManWithTheMarkActivate() + { + return !CheckWhetherNegated() && DefaultCheckWhetherBotCanSearch(); + } + + public bool Level4MonsterSummon() + { + if (!Card.IsCode(CardId.PrimiteDragonEtherBeryl, CardId.TheManWithTheMark)) + { + return false; + } + + bool canSummonDragon = Bot.HasInHand(CardId.PrimiteDragonEtherBeryl); + if (!activatedCardIdList.Contains(CardId.PrimiteLordlyLode) && DefaultCheckWhetherBotCanSearch()) + { + canSummonDragon |= Bot.HasInHand(CardId.PrimiteLordlyLode) && Bot.GetSpellCountWithoutField() < 5; + canSummonDragon |= Bot.GetSpells().Any(c => c.IsCode(CardId.PrimiteLordlyLode) && c.IsFacedown()); + } + if (canSummonDragon) + { + bool summonFlag = false; + // summon to search? + if (!CheckWhetherNegated(true, true) && CheckWhetherCanActivateMonsterEffect(CardAttribute.Earth)) + { + summonFlag |= !activatedCardIdList.Contains(CardId.PrimiteLordlyLode) && !Bot.HasInHandOrInSpellZone(CardId.PrimiteLordlyLode) && CheckRemainInDeck(CardId.PrimiteLordlyLode) > 0; + summonFlag |= CheckRemainInDeck(CardId.PrimiteDrillbeam) > 0; + } + + // summon to recycle beam + if (!Bot.HasInMonstersZone(CardId.PrimiteDragonEtherBeryl, faceUp: true) && !activatedCardIdList.Contains(CardId.PrimiteDrillbeam + 1) + && Bot.HasInGraveyard(CardId.PrimiteDrillbeam) && Bot.GetSpellCountWithoutField() < 5) + { + summonFlag = true; + } + + if (summonFlag && Card.IsCode(CardId.PrimiteDragonEtherBeryl)) + { + summonCount --; + return true; + } + } + + bool canSummonMan = Bot.HasInHand(CardId.TheManWithTheMark); + canSummonMan |= Bot.HasInHand(CardId.AnubisTheLastJudge) && DefaultCheckWhetherBotCanSearch() && CheckRemainInDeck(CardId.TheManWithTheMark) > 0 && !activatedCardIdList.Contains(CardId.AnubisTheLastJudge); + if (Bot.HasInHandOrInSpellZone(CardId.TreasuresOfTheKings) && !activatedCardIdList.Contains(CardId.TreasuresOfTheKings + 1) && DefaultCheckWhetherBotCanSearch() && CheckRemainInDeck(CardId.TheManWithTheMark) > 0) + { + canSummonMan |= Bot.Graveyard.Any(c => c.IsTrap()); + int facedownCardCount = Bot.GetSpells().Count(c => c.IsFacedown()); + facedownCardCount += Bot.GetMonsters().Count(c => c.IsFacedown()); + facedownCardCount += GetCanSetSpells().Count(); + canSummonMan |= facedownCardCount >= 2; + } + if (canSummonMan && Card.IsCode(CardId.TheManWithTheMark)) + { + summonCount --; + return true; + } + + // summon for synchro level 10 monster + if (Bot.ExtraDeck.Any(c => c.HasType(CardType.Synchro) && c.Level == 10)) + { + // have labradorite dragon + bool haveTunerFlag = Bot.GetMonsters().Any(c => c.IsFaceup() && c.IsCode(CardId.LabradoriteDragon) && c.IsTuner()); + haveTunerFlag |= !activatedCardIdList.Contains(CardId.PrimiteLordlyLode + 1) && Bot.HasInSpellZone(CardId.PrimiteLordlyLode, true, true); + if (haveTunerFlag) + { + summonCount --; + return true; + } + } + + return false; + } + + public bool PotOfExtravaganceActivate() + { + if (CheckWhetherNegated()) return false; + SelectSTPlace(Card, true); + AI.SelectOption(1); + return true; + } + + public bool TerraformingActivate() + { + if (CheckWhetherNegated()) return false; + SelectSTPlace(Card, true); + return true; + } + + public bool PrimiteDrillbeamActivate() + { + if (Card.Location == CardLocation.Grave) + { + if (CheckWhetherNegated()) return false; + + // check whether can active + bool canActivate = Bot.HasInHand(CardId.PrimiteLordlyLode) || Bot.HasInHand(CardId.PrimiteDragonEtherBeryl); + canActivate |= Bot.HasInSpellZone(CardId.ApophisTheSerpent) || Bot.HasInSpellZone(CardId.ApophisTheSwampDeity); + canActivate |= Bot.GetMonsters().Any(c => c.IsFaceup() && c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity, CardId.LabradoriteDragon)); + canActivate |= Bot.HasInHand(CardId.ApophisTheSwampDeity) && Bot.GetSpellCountWithoutField() <= 3; + + if (canActivate) { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + // cannot activate + return false; + } + + // negate + if (CheckWhetherNegated(true, true, CardType.Spell)) + { + return false; + } + + bool activateFlag = false; + + // negate problematic enemy card + List problematicEnemyCardList = GetProblematicEnemyCardList(true, false, CardType.Spell); + if (problematicEnemyCardList.Count(c => !c.IsDisabled()) > 0) { + problematicEnemyCardList.RemoveAll(c => currentNegateCardList.Contains(c)); + if (problematicEnemyCardList.Count > 0) { + activateFlag = true; + } + } + + // negate cards on chain + foreach (ClientCard card in Duel.CurrentChain) { + if (card.Controller == 1 && card.IsOnField() && card.IsFaceup() && !card.IsDisabled() && !currentNegateCardList.Contains(card) + && CheckCanBeTargeted(card, true, CardType.Spell) && CheckCardShouldNegate(card)) { + activateFlag = true; + } + } + + // can recycle, so activate it + if (Bot.HasInMonstersZone(CardId.PrimiteDragonEtherBeryl, faceUp: true) && !activatedCardIdList.Contains(CardId.PrimiteDrillbeam + 1) + && (CurrentTiming & hintToHand) == 0) + { + List targetList = GetNormalEnemyTargetList(true, true, CardType.Spell, true); + if (targetList.Count > 0) { + activateFlag = true; + } + } + + // become target + if (DefaultOnBecomeTarget()) { + activateFlag |= Enemy.GetSpells().Any(c => c.IsFaceup() && !c.IsDisabled() && CheckCanBeTargeted(c, true, CardType.Spell)); + activateFlag |= Enemy.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled() && CheckCanBeTargeted(c, true, CardType.Spell)); + } + + if (Duel.Phase == DuelPhase.BattleStep) + { + // remove problematic enemy monster + ClientCard problematicEnemyMonster = GetProblematicEnemyMonster(0, true, true, CardType.Spell); + if (problematicEnemyMonster != null && !problematicEnemyMonster.IsDisabled()) + { + activateFlag = true; + } + } + + if (activateFlag) + { + activatedCardIdList.Add(Card.Id); + SelectSTPlace(Card, true); + return true; + } + + return false; + } + + public bool PrimiteLordlyLodeActivate() + { + if (Card.Location == CardLocation.SpellZone && Card.IsFaceup()) + { + return false; + } + + // activate + bool activateFlag = PrimiteLordlyLodeActivateCheck(); + Logger.DebugWriteLine("PrimiteLordlyLodeActivate: " + activateFlag); + if (activateFlag) + { + SelectSTPlace(Card, true); + activatedCardIdList.Add(Card.Id); + return true; + } + return false; + } + + public bool PrimiteLordlyLodeActivateCheck() + { + if (CheckWhetherNegated(true, true, CardType.Spell)) return false; + bool activateFlag = false; + if (Bot.HasInHandOrHasInMonstersZone(CardId.PrimiteDragonEtherBeryl) && DefaultCheckWhetherBotCanSearch()) + { + // for search drillbeam + activateFlag |= CheckRemainInDeck(CardId.PrimiteDrillbeam) > 0; + activateFlag |= summonCount <= 0 && Card.Location == CardLocation.SpellZone && Card.IsFacedown(); + } + if (summonCount > 0 && !Bot.HasInHand(CardId.PrimiteDragonEtherBeryl) && CheckRemainInDeck(CardId.PrimiteDragonEtherBeryl) > 0 && DefaultCheckWhetherBotCanSearch()) + { + // for search ether beryl + activateFlag |= Bot.HasInGraveyard(CardId.PrimiteDrillbeam); + activateFlag |= CheckWhetherCanActivateMonsterEffect(CardAttribute.Earth) && !CheckWhetherNegated(true, true, CardType.Monster); + } + if (!Bot.HasInSpellZone(CardId.PrimiteLordlyLode, true, true)) + { + // for activate it + activateFlag |= DefaultCheckWhetherBotCanSearch(); + + // for special summon + CardLocation loc; + if (Bot.HasInHand(CardId.LabradoriteDragon)) + { + loc = CardLocation.Hand; + } + else if (CheckRemainInDeck(CardId.LabradoriteDragon) > 0) + { + loc = CardLocation.Deck; + } + else if (Bot.HasInGraveyard(CardId.LabradoriteDragon)) + { + loc = CardLocation.Grave; + } else { + return false; + } + int drawCount = GetSpecialSummonDrawCount(loc); + if (drawCount < 2) + { + activateFlag |= Bot.GetMonsters().Any(c => c.IsFaceup() && c.Level == 4 && !c.HasType(CardType.Xyz | CardType.Link)) + && Bot.ExtraDeck.Any(c => c.IsFaceup() && c.Level == 10 && c.HasType(CardType.Synchro)); + } + } + if (Card.Location == CardLocation.SpellZone && Card.IsFacedown()) + { + activateFlag |= DefaultCheckWhetherBotCanSearch(); + } + return activateFlag; + } + + public bool PrimiteLordlyLodeSpSummon() + { + if (Card.Location == CardLocation.SpellZone && Card.IsFaceup()) + { + // add activating flag + if (CheckWhetherNegated()) return false; + if (!PrimiteLordlyLodeSpSummonCheck()) return false; + activatingLodeSpSummonEffect = true; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool PrimiteLordlyLodeSpSummonCheck() + { + // special summon + CardLocation loc; + if (Bot.HasInHand(CardId.LabradoriteDragon)) + { + loc = CardLocation.Hand; + } + else if (CheckRemainInDeck(CardId.LabradoriteDragon) > 0) + { + loc = CardLocation.Deck; + } + else if (Bot.HasInGraveyard(CardId.LabradoriteDragon)) + { + loc = CardLocation.Grave; + } else { + return false; + } + int drawCount = GetSpecialSummonDrawCount(loc); + return drawCount < 2; + } + + public bool TreasuresOfTheKingsActivate() + { + if (CheckWhetherNegated()) return false; + + // search + if (ActivateDescription == Util.GetStringId(CardId.TreasuresOfTheKings, 0)) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + // activate + bool activateFlag = false; + if (Bot.GetSpellCountWithoutField() == 5) + { + // for search + if (!DefaultCheckWhetherBotCanSearch() || CheckRemainInDeck(CardId.TheManWithTheMark, CardId.AnubisTheLastJudge) == 0) + { + activateFlag = false; + } + else + { + bool hasOtherSpellTrapInHand = + Bot.Hand.Any(c => (c.IsSpell() || c.IsTrap()) && c != Card); + + bool gyHasTrap = + Bot.Graveyard.Any(c => c.IsTrap()); + + int otherFacedownOnField = + Bot.GetMonsters().Count(m => m.IsFacedown()) + + Bot.GetSpells().Count(s => s.IsFacedown() && s != Card); + + activateFlag = gyHasTrap || hasOtherSpellTrapInHand || otherFacedownOnField >= 2; + } + } + else + { + // for set + if (CheckRemainInDeck(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity) > 0) + activateFlag = true; + } + + return activateFlag; + } + + public bool DominusSparkActivate() + { + if (CheckWhetherNegated()) return false; + // activate from field first + if (Duel.MainPhase.ActivableCards.Any(c => c.IsCode(CardId.DominusSpark) && c.IsOnField() && c != Card + && !infiniteImpermanenceNegatedColumns.Contains(c.Sequence))) + { + return false; + } + + bool shouldActivate = false; + // check whether there is a dangerous monster + ClientCard dangerousMonster = GetProblematicEnemyMonster(-1, true, false, CardType.Trap); + shouldActivate |= dangerousMonster != null; + + if (Duel.Phase == DuelPhase.BattleStep && Duel.Player == 1) + { + dangerousMonster = GetProblematicEnemyMonster(0, true, false, CardType.Trap); + shouldActivate |= dangerousMonster != null; + } + + // check whether it is the end phase and there is a monster on the enemy field + if (Duel.Phase == DuelPhase.End && Duel.Player == 1 && Duel.Turn == 1) + { + List endPhaseTargets = GetNormalEnemyTargetList(canBeTarget: true, ignoreCurrentDestroy: true, selfType: CardType.Trap); + shouldActivate |= endPhaseTargets.Any(c => c.IsMonster() && c.IsFaceup()); + } + + shouldActivate |= DefaultOnBecomeTarget() && !CheckWhetherNegated(); + + if (shouldActivate) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + + ClientCard LastChainCard = Util.GetLastChainCard(); + + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = Card.Sequence; + int that_seq = -1; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone) that_seq = LastChainCard.Sequence; + if ((this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || Util.IsChainTarget(Card) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + return true; + } + } + + // negate monster + List shouldNegateList = GetMonsterListForTargetNegate(true, CardType.Trap); + if (shouldNegateList.Count > 0) + { + SelectSTPlace(Card, true); + return true; + } + + return false; + } + + public bool DominusNegateTrapActivate() + { + if (CheckWhetherNegated()) return false; + // activate from field first + if (Duel.MainPhase.ActivableCards.Any(c => c.IsCode(Card.Id) && c.IsOnField() && c != Card + && !infiniteImpermanenceNegatedColumns.Contains(c.Sequence))) + { + return false; + } + + ClientCard LastChainCard = Util.GetLastChainCard(); + if (LastChainCard != null && LastChainCard.Controller == 1 && CheckCardShouldNegate(LastChainCard)) + { + currentNegateCardList.Add(LastChainCard); + SelectSTPlace(Card, true); + if (Card.IsCode(CardId.SongsOfTheDominators) && Card.Location == CardLocation.Hand) + { + songsOfTheDominatorsActivatedFromHand = true; + } + return true; + } + + return false; + } + + public bool SongsOfTheDominatorsActivateFirst() + { + return Bot.Graveyard.Any(c => c.HasType(CardType.Trap)) && DominusNegateTrapActivate(); + } + + public bool ApophisTheSwampDeityActivate() + { + return ApophisTheSwampDeityActivateCheck(1); + } + + public bool ApophisTheSwampDeityActivateForAttack() + { + return ApophisTheSwampDeityActivateCheck(2); + } + + public bool ApophisTheSwampDeityActivateCheck(int activatePriority = 0) + { + if (CheckWhetherNegated()) return false; + if (currentSummoningCount + Bot.GetMonsters().Count(c => c.Sequence < 5) >= 5) return false; + + int canNegateCount = Bot.GetSpells().Count(c => c != Card && + (c.IsCode(CardId.ApophisTheSwampDeity) + || (c.IsCode(CardId.ApophisTheSerpent) && + (c.IsFaceup() || !activatedCardIdList.Contains(CardId.ApophisTheSerpent))))); + canNegateCount += Bot.GetMonsters().Count(c => c.IsCode(new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity }) && c.IsFaceup()); + + bool shouldActivate = false; + // for negate + if (Duel.CurrentChain.Count > 0 && canNegateCount > 0) + { + // exists cards to negate? + foreach (ClientCard chain in Duel.CurrentChain) + { + if (chain.IsFaceup() && chain.IsOnField() && chain.Controller == 1 && !currentNegateCardList.Contains(chain)) + { + Logger.DebugWriteLine("[ApophisTheSwampDeity] Negate card on chain: " + chain.Name); + shouldActivate = true; + break; + } + } + } + + // for summon divine serpent + if (DivineSerpentApophisSpSummonCheck()) { + int newApophisCount = Duel.MainPhase.ActivableCards.Count(c => c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity)); + int currentApophisCount = Bot.GetMonsters().Count(c => c.IsFaceup() && + (c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity) || (c.IsCode(CardId.DivineSerpentApophis) && activatedDivineSerpent1stList.Contains(c)))); + bool checkResult = currentApophisCount < 2 && currentApophisCount + newApophisCount >= 2; + if (checkResult) + { + Logger.DebugWriteLine("[ApophisTheSwampDeity] DivineSerpentApophisSpSummonCheck: " + checkResult); + } + shouldActivate |= checkResult; + } + + // for little knight + if (Bot.ExtraDeck.Any(c => c.IsCode(CardId.SPLittleKnight)) && !SPLittleKnightSummonCheck() && SPLittleKnightSummonCheck(true) + && Duel.Player == 0 && Bot.ExtraDeck.Any(c => c.IsCode(CardId.LinkSpider))) + { + bool checkResult = Bot.GetMonsters().Count(c => c.IsFaceup() && c.HasType(CardType.Effect)) == 1; + if (checkResult) + { + Logger.DebugWriteLine("[ApophisTheSwampDeity] SPSmallKnightSummonCheck: " + checkResult); + } + shouldActivate |= checkResult; + } + + // for triggering divine serpent apophis + if (CheckWhetherCanActivateMonsterEffect(CardAttribute.Earth) && !CheckWhetherNegated(true, true, CardType.Monster) + && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled() && c.IsCode(CardId.DivineSerpentApophis) && !activatedDivineSerpent2ndList.Contains(c))) + { + bool checkResult = GetProblematicEnemyCardList(true, false, CardType.Monster).Count() > 0 + && !Duel.CurrentChain.Any(c => c.Controller == 0 && c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity)) + && !Duel.MainPhase.ActivableCards.Any(c => c.IsCode(CardId.ApophisTheSerpent)); + if ((CurrentTiming & hintTimingMainEnd) != 0 && Duel.Player == 1) + { + checkResult |= GetNormalEnemyTargetList(true, true, CardType.Monster).Count() > 0 + && !Duel.CurrentChain.Any(c => c.Controller == 0 && c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity)) + && !Duel.MainPhase.ActivableCards.Any(c => c.IsCode(CardId.ApophisTheSerpent)); + } + if (checkResult) + { + Logger.DebugWriteLine("[ApophisTheSwampDeity] DivineSerpentApophisActivate: " + checkResult); + } + shouldActivate |= checkResult; + } + + // for triggering silhouette hat rabbit + if (CheckWhetherCanActivateMonsterEffect(CardAttribute.Light) && !CheckWhetherNegated(true, true, CardType.Monster, true) + && !activatedCardIdList.Contains(CardId.SilhouhatteRabbit + 1) + && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled() && c.IsCode(CardId.SilhouhatteRabbit))) + { + bool checkResult = GetProblematicEnemyCardList(true, false, CardType.Monster).Count(c => c.HasType(CardType.Spell | CardType.Trap)) > 0 + && !Duel.CurrentChain.Any(c => c.Controller == 0 && c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity)) + && !Duel.MainPhase.ActivableCards.Any(c => c.IsCode(CardId.ApophisTheSerpent)); + if ((CurrentTiming & hintTimingMainEnd) != 0 && Duel.Player == 1) + { + checkResult |= GetNormalEnemySpellTargetList(true, true, CardType.Monster).Count() > 0 + && !Duel.CurrentChain.Any(c => c.Controller == 0 && c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity)) + && !Duel.MainPhase.ActivableCards.Any(c => c.IsCode(CardId.ApophisTheSerpent)); + } + if (checkResult) + { + Logger.DebugWriteLine("[ApophisTheSwampDeity] SilhouhatteRabbitSummonCheck: " + checkResult); + } + shouldActivate |= checkResult; + } + + // for triggering primite drillbeam + if (Bot.HasInHandOrInSpellZone(CardId.PrimiteDrillbeam) && !activatedCardIdList.Contains(CardId.PrimiteDrillbeam)) + { + // whether have other cards to trigger primite beam + bool canTriggerPrimiteBeam = Bot.Hand.Any(c => c.IsCode(CardId.PrimiteDragonEtherBeryl, CardId.PrimiteLordlyLode, CardId.LabradoriteDragon)); + canTriggerPrimiteBeam |= Bot.HasInMonstersZone(new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity, CardId.LabradoriteDragon }, faceUp: true); + canTriggerPrimiteBeam |= Bot.GetSpells().Any(c => c != Card && c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity) && (c.IsFacedown() || Duel.CurrentChain.Contains(c))); + + bool checkResult = !canTriggerPrimiteBeam; + if (checkResult) + { + Logger.DebugWriteLine("[ApophisTheSwampDeity] PrimiteDrillbeamSummonCheck: " + checkResult); + } + shouldActivate |= !canTriggerPrimiteBeam; + } + + // TODO for other summon + + // for attack + if (Duel.Turn > 1 && Duel.Phase == DuelPhase.Main1 && Enemy.GetMonsterCount() == 0 && Duel.Player == 0 && Duel.CurrentChain.Count == 0 + && activatePriority >= 2) + { + Logger.DebugWriteLine("[ApophisTheSwampDeity] Attack: " + true); + shouldActivate |= true; + } + + // for defense + if (Duel.Phase == DuelPhase.Main1 && Bot.GetMonsterCount() == 0 && Enemy.GetMonsters().Sum(c => c.Attack) >= Bot.LifePoints + && (CurrentTiming & hintTimingMainEnd) != 0) + { + Logger.DebugWriteLine("[ApophisTheSwampDeity] Defense: " + true); + shouldActivate |= true; + } + + if (shouldActivate) + { + // mark cards to negate + int negateCount = Bot.GetSpells().Count(c => c != Card && c.IsFaceup() && c.HasType(CardType.Continuous) && c.HasType(CardType.Trap)); + negateCount += Bot.GetMonsters().Count(c => c.IsFaceup() && c.HasType(CardType.Continuous) && c.HasType(CardType.Trap)); + // negate cards on chain fist + if (negateCount > 0) + { + foreach (ClientCard chain in Duel.CurrentChain) + { + if (chain.IsFaceup() && chain.IsOnField() && !currentNegateCardList.Contains(chain) && CheckCardShouldNegate(chain)) + { + negateCount--; + currentNegateCardList.Add(chain); + if (negateCount <= 0) break; + } + } + } + + if (negateCount > 0) + { + // Find negatable enemy spell/trap cards on field that are not yet negated + List negatableSpellsTraps = Enemy.GetSpells().Where(c => + c.IsFaceup() && + !c.IsDisabled() && + !currentNegateCardList.Contains(c) + ).ToList(); + + // Mark negatable spell/trap cards as negated, up to the available count + foreach (ClientCard card in negatableSpellsTraps) + { + currentNegateCardList.Add(card); + negateCount--; + if (negateCount <= 0) break; + } + } + + if (negateCount > 0) + { + // Find negatable enemy monsters on field that are not yet negated + List negatableMonsters = Enemy.GetMonsters().Where(c => + c.IsFaceup() && + !c.IsDisabled() && + !currentNegateCardList.Contains(c) + ).ToList(); + + // Mark negatable monsters as negated, up to the remaining count + foreach (ClientCard card in negatableMonsters) + { + currentNegateCardList.Add(card); + negateCount--; + if (negateCount <= 0) break; + } + } + + currentSummoningCount++; + return true; + } + + return false; + } + + public bool ApophisTheSerpentActivate() + { + if (CheckWhetherNegated()) return false; + if (currentSummoningCount + Bot.GetMonsters().Count(c => c.Sequence < 5) >= 5) return false; + if (Bot.GetSpells().Any(c => c.IsFacedown() && c.IsCode(CardId.ApophisTheSwampDeity) && !infiniteImpermanenceNegatedColumns.Contains(c.Sequence))) + { + if (!(Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2)) + { + return false; + } + } + currentSummoningCount++; + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool SpellNegateActivate() + { + if (CheckWhetherNegated() || Duel.LastChainPlayer != 1) return false; + + ClientCard prevChainCard = Util.GetLastChainCard(); + if (prevChainCard != null && !CheckCardShouldNegate(prevChainCard)) + { + return false; + } + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool SolemnReportBanishActivate() + { + if (SpellNegateActivate()) + { + ClientCard prevChainCard = Util.GetLastChainCard(); + if (prevChainCard != null && prevChainCard.IsCode(solemnReportBanishIdList)) + { + return true; + } + } + return false; + } + + public bool DivineSerpentApophisSpSummonCheck() + { + if (Duel.Player != 0) return false; + bool checkFlag = false; + // recycle + if (CheckWhetherCanActivateMonsterEffect(CardAttribute.Earth) && !CheckWhetherNegated(true, true, CardType.Monster) + && Bot.GetSpellCountWithoutField() < 5) + { + checkFlag |= Bot.HasInGraveyard(new List { CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity }); + checkFlag |= !CheckWhetherWillbeRemoved(); + } + + // for attack + checkFlag |= GetBotBestPower(true) <= Util.GetBestPower(Enemy) && Util.GetBestPower(Enemy) < 2800; + + return checkFlag; + } + + public bool DivineSerpentApophisSpSummon() + { + if (!DivineSerpentApophisSpSummonCheck()) return false; + // select material with different name + ClientCard apophisTheSerpent = Bot.GetMonsters().FirstOrDefault(c => c.IsFaceup() && c.IsCode(CardId.ApophisTheSerpent)); + ClientCard apophisTheSwampDeity = Bot.GetMonsters().FirstOrDefault(c => c.IsFaceup() && c.IsCode(CardId.ApophisTheSwampDeity)); + AI.SelectMaterials(new List { apophisTheSerpent, apophisTheSwampDeity }, HintMsg.Release); + return true; + } + + public bool DivineSerpentApophisActivate() + { + if (CheckWhetherNegated()) return false; + if (ActivateDescription == Util.GetStringId(CardId.DivineSerpentApophis, 0)) + { + // set trap + activatedDivineSerpent1stList.Add(Card); + } + else + { + // destroy + List targetList = GetNormalEnemyTargetList(true, false, CardType.Monster); + if (targetList.Count() == 0) + { + return false; + } + activatedDivineSerpent2ndList.Add(Card); + } + return true; + } + + public bool Level10SynchroSummon() + { + if (!Level10SynchroSummonCheck()) return false; + if (!Card.IsCode(new List { CardId.BaronneDeFleur, CardId.SwordsoulSupremeSovereignChengying })) + { + return false; + } + + int decideSummonId = 0; + // select which to summon + if (GetBotBestPower(true) < Util.GetBestPower(Enemy) && Util.GetBestPower(Enemy) > 3000 + && (!CheckWhetherCanActivateMonsterEffect(CardAttribute.Wind) || lodeSpSummonEffectResolved) + && Duel.Phase == DuelPhase.Main1 && Duel.Turn > 1) + { + int banishCount = Bot.Banished.Count() + Enemy.Banished.Count(); + if (3000 + banishCount * 200 >= Util.GetBestPower(Enemy) + && Duel.MainPhase.SummonableCards.Any(c => c.IsCode(CardId.SwordsoulSupremeSovereignChengying)) + && !Card.IsCode(CardId.SwordsoulSupremeSovereignChengying)) + { + return false; + } + decideSummonId = CardId.SwordsoulSupremeSovereignChengying; + } + if (decideSummonId == 0 && CheckWhetherCanActivateMonsterEffect(CardAttribute.Wind) + && Duel.MainPhase.SummonableCards.Any(c => c.IsCode(CardId.BaronneDeFleur)) + && !Card.IsCode(CardId.BaronneDeFleur)) + { + return false; + } + if (decideSummonId == 0 && !CheckWhetherCanActivateMonsterEffect(CardAttribute.Wind) + && Duel.MainPhase.SummonableCards.Any(c => c.IsCode(CardId.SwordsoulSupremeSovereignChengying)) + && !Card.IsCode(CardId.SwordsoulSupremeSovereignChengying)) + { + return false; + } + + ClientCard level4Monster = Bot.GetMonsters().FirstOrDefault(c => c.IsFaceup() && c.Level == 4 && c.IsCode(CardId.PrimiteDragonEtherBeryl)); + + if (level4Monster == null) + { + // find level4 monster with lowest power + level4Monster = Bot.GetMonsters().OrderBy(c => c.GetDefensePower()).FirstOrDefault(c => c.IsFaceup() && c.Level == 4 && !c.HasType(CardType.Xyz | CardType.Link)); + } + if (level4Monster == null) + { + return false; + } + + ClientCard level6Tuner = Bot.GetMonsters().OrderBy(c => c.GetDefensePower()).FirstOrDefault(c => c.IsFaceup() && c.Level == 6 && c.IsTuner()); + if (level6Tuner == null) + { + return false; + } + AI.SelectMaterials(new List { level4Monster, level6Tuner }); + return true; + } + + public bool Level10SynchroSummonCheck() + { + if (CheckShouldNoMoreSpSummon()) return false; + if (GetSpecialSummonDrawCount(CardLocation.Extra) > 1 && CheckAtAdvantage()) return false; + + return true; + } + + public bool SwordsoulSupremeSovereignChengyingActivate() + { + if (CheckWhetherNegated()) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool BaronneDeFleurNegateEffect() + { + if (ActivateDescription != Util.GetStringId(CardId.BaronneDeFleur, 1)) + { + return false; + } + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + ClientCard lastChainCard = Util.GetLastChainCard(); + if (Duel.LastChainPlayer == 1 && lastChainCard != null) + { + if (CheckAtAdvantage() && lastChainCard.IsCode(new List {_CardId.MaxxC, _CardId.MulcharmyFuwalos, _CardId.MulcharmyNyalus, _CardId.MulcharmyPurulia})) + { + return false; + } + if (Duel.LastChainTargets.Contains(Card) && lastChainCard.IsCode(_CardId.EffectVeiler, _CardId.InfiniteImpermanence, _CardId.BreakthroughSkill)) + { + return false; + } + } + currentNegateCardList.Add(lastChainCard); + return true; + } + + public bool BaronneDeFleurActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.BaronneDeFleur, 1)) + { + // negate + return false; + } else if (Duel.Phase == DuelPhase.Standby) + { + // special summon + // TODO waiting for handle OnCardHint + return false; + } else { + // destroy + List targetList = GetNormalEnemyTargetList(); + if (targetList.Count() > 0) + { + return true; + } + } + + return false; + } + + public bool SuperdreadnoughtRailCannonJuggernautLiebeActivate() + { + if (CheckWhetherNegated()) return false; + return true; + } + + public bool SuperdreadnoughtRailCannonJuggernautLiebeSpSummon() + { + if (!SuperdreadnoughtRailCannonJuggernautLiebeSpSummonCheck()) return false; + return true; + } + + public bool SuperdreadnoughtRailCannonJuggernautLiebeSpSummonCheck() + { + int enemyPower = Util.GetBestPower(Enemy); + int botPower = GetBotBestPower(true); + if (botPower < enemyPower) + { + int currentAttack = 4000; + if (!CheckWhetherNegated(true, true, CardType.Monster) && CheckWhetherCanActivateMonsterEffect(CardAttribute.Earth) && !lodeSpSummonEffectResolved) + { + currentAttack += 2000; + } + if (currentAttack >= enemyPower) + { + return true; + } + } + return false; + } + + public bool SuperdreadnoughtRailCannonFlyingLauncherActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.SuperdreadnoughtRailCannonFlyingLauncher, 2)) + { + List targetList = GetNormalEnemySpellTargetList(true, false, CardType.Monster); + if (targetList.Count() > 0) + { + return true; + } + } + return false; + } + + public bool SuperdreadnoughtRailCannonFlyingLauncherSpSummon() + { + bool hasLiebeInExtra = Bot.ExtraDeck.Any(c => c.IsCode(CardId.SuperdreadnoughtRailCannonJuggernautLiebe)); + int enemyPower = Util.GetBestPower(Enemy); + int botPower = GetBotBestPower(true); + bool needSummonLiebe = SuperdreadnoughtRailCannonJuggernautLiebeSpSummonCheck(); + + if ((hasLiebeInExtra && needSummonLiebe) + || (!hasLiebeInExtra && botPower < enemyPower && enemyPower <= 3800) + || (CheckWhetherCanActivateMonsterEffect(CardAttribute.Earth) + && !CheckWhetherNegated(true, true, CardType.Monster) + && !lodeSpSummonEffectResolved + && GetNormalEnemySpellTargetList(true, false, CardType.Monster).Count() > 0)) + { + return true; + } + return false; + } + + public bool EvilswarmExcitonKnightSummon() + { + if (CheckWhetherNegated(true, true, CardType.Monster) || !CheckWhetherCanActivateMonsterEffect(CardAttribute.Light)) return false; + int selfCount = Bot.GetMonsterCount() + Bot.GetSpellCount() + Bot.GetHandCount(); + int oppoCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount() + Enemy.GetHandCount(); + return (selfCount - 1 < oppoCount) && DefaultEvilswarmExcitonKnightEffect(); + } + + public bool SPLittleKnightActivate() + { + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.SPLittleKnight, 0)) + { + // banish card + List problemCardList = GetProblematicEnemyCardList(true, selfType: CardType.Monster); + problemCardList.AddRange(GetNormalEnemyTargetList(true, true, CardType.Monster)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => card.HasType(CardType.Monster)).OrderByDescending(card => card.Attack)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => !card.HasType(CardType.Monster))); + if (problemCardList.Count() > 0) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } else if (ActivateDescription == Util.GetStringId(CardId.SPLittleKnight, 1)) + { + ClientCard selfMonster = null; + foreach (ClientCard target in Bot.GetMonsters()) + { + if (Duel.ChainTargets.Contains(target)) + { + selfMonster = target; + break; + } + } + if (selfMonster == null) + { + if (Duel.Player == 1) + { + selfMonster = Bot.GetMonsters().Where(card => card.IsAttack()).OrderBy(card => card.Attack).FirstOrDefault(); + if (!Util.IsOneEnemyBetterThanValue(selfMonster.Attack, true)) selfMonster = null; + } + } + if (selfMonster != null) + { + ClientCard nextMonster = null; + List selfTargetList = Bot.GetMonsters().Where(card => card != selfMonster).ToList(); + if (Enemy.GetMonsterCount() == 0 && selfTargetList.Count() > 0) + { + selfTargetList.Sort(CompareUsableAttack); + nextMonster = selfTargetList[0]; + } + if (Enemy.GetMonsterCount() > 0) + { + nextMonster = GetProblematicEnemyMonster(0, true, false, CardType.Monster); + } + if (nextMonster != null) + { + SPLittleKnightRemoveStep = 1; + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + + return false; + } + + public bool SPLittleKnightSummon() + { + if (CheckWhetherNegated(true, true, CardType.Monster, true) || !CheckWhetherCanActivateMonsterEffect(CardAttribute.Dark)) return false; + + List effectMonsters = Bot.GetMonsters().Where(c => c.IsFaceup() && c.HasType(CardType.Effect) + && (!c.IsCode(CardId.SilhouhatteRabbit) || !summonThisTurn.Contains(c)) + ).OrderBy(c => c.GetDefensePower()).ToList(); + + // summon to remove problem cards + if (SPLittleKnightSummonCheck() || Util.IsTurn1OrMain2() && (Enemy.GetMonsterCount() + Enemy.GetSpellCount() + Enemy.Graveyard.Count > 0)) + { + ClientCard extraMonster = null; + ClientCard otherMonster = null; + foreach (ClientCard monster in effectMonsters) + { + // skip important monster if needed + if (Util.IsTurn1OrMain2() && monster.IsCode(new List { CardId.BaronneDeFleur, CardId.SwordsoulSupremeSovereignChengying, CardId.DivineSerpentApophis })) + { + continue; + } + + if (monster.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)) + { + if (extraMonster != null) + { + otherMonster = monster; + } + else + { + extraMonster = monster; + } + } + else + { + otherMonster = monster; + } + + if (extraMonster != null && otherMonster != null) + { + break; + } + } + + if (extraMonster != null && otherMonster != null) + { + AI.SelectMaterials(new List { extraMonster, otherMonster }); + return true; + } + } + + return false; + } + + public bool SPLittleKnightSummonCheck(bool ignoreMaterialCheck = false) + { + if (CheckWhetherNegated(true, true, CardType.Monster, true) + || !CheckWhetherCanActivateMonsterEffect(CardAttribute.Dark) + || lodeSpSummonEffectResolved) return false; + // banish card + List problemCardList = GetProblematicEnemyCardList(true, selfType: CardType.Monster, ignoreSpells: !Util.IsTurn1OrMain2()); + bool summonFlag = problemCardList.Count() > 0; + ClientCard problematicEnemyMonster = GetProblematicEnemyMonster(0, true, false, CardType.Monster); + if (problematicEnemyMonster != null) + { + summonFlag = true; + } + if (summonFlag) + { + if (ignoreMaterialCheck) + { + return true; + } + // check material + return Bot.GetMonsters().Any(c => c.IsFaceup() && c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) + && (!c.IsCode(CardId.SilhouhatteRabbit) || !summonThisTurn.Contains(c))) + && Bot.GetMonsters().Count(c => c.IsFaceup() && c.HasType(CardType.Effect)) >= 2; + } + + return false; + } + + public bool SilhouhatteRabbitActivate() + { + if (CheckWhetherNegated()) return false; + if (ActivateDescription == Util.GetStringId(CardId.SilhouhatteRabbit, 0) || ActivateDescription == -1) + { + // set + // default to true + activatedCardIdList.Add(Card.Id); + return true; + } else + { + // destroy + List targetList = GetNormalEnemySpellTargetList(true, false, CardType.Monster, false); + if (targetList.Count() > 0) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + return false; + } + + public bool SilhouhatteRabbitSummon() + { + if (CheckWhetherNegated(true, true, CardType.Monster, true) || !CheckWhetherCanActivateMonsterEffect(CardAttribute.Light)) return false; + if (!SilhouhatteRabbitSummonCheck()) return false; + // select material + ClientCard anubis = Bot.GetMonsters().FirstOrDefault(c => c.IsFaceup() && c.IsCode(CardId.AnubisTheLastJudge)); + List materialList = new List { }; + if (anubis != null) + { + materialList.Add(anubis); + } + // select remain material + List effectMonsters = Bot.GetMonsters().Where(c => c.IsFaceup() && c.HasType(CardType.Effect) + && (!c.IsCode(CardId.SilhouhatteRabbit) || !summonThisTurn.Contains(c)) + ).OrderBy(c => c.GetDefensePower()).ToList(); + if (effectMonsters.Count() > 0) + { + foreach (ClientCard monster in effectMonsters) + { + // skip important monster if needed + if (monster.IsCode(new List { CardId.BaronneDeFleur, CardId.SwordsoulSupremeSovereignChengying, CardId.DivineSerpentApophis, CardId.SPLittleKnight })) + { + continue; + } + materialList.Add(monster); + if (materialList.Count() >= 2) + { + break; + } + } + } + + if (materialList.Count() >= 2) + { + AI.SelectMaterials(materialList); + return true; + } + return false; + } + + public bool SilhouhatteRabbitSummonCheck(bool ignoreMaterialCheck = false) + { + if (CheckWhetherNegated(true, true, CardType.Monster, true) + || !CheckWhetherCanActivateMonsterEffect(CardAttribute.Light) + || lodeSpSummonEffectResolved) return false; + + List problemCardList = GetProblematicEnemyCardList(true, selfType: CardType.Monster); + if (problemCardList.Count() > 0) + { + return false; + } + int remainApophisCount = CheckRemainInDeck(CardId.ApophisTheSwampDeity); + if (remainApophisCount == 0) + { + return false; + } + if (Bot.Hand.Any(c => c.IsCode(CardId.ApophisTheSwampDeity)) || Bot.GetSpells().Any(c => c.IsCode(CardId.ApophisTheSwampDeity) && c.IsFacedown())) + { + return false; + } + if (activatedCardIdList.Contains(CardId.SilhouhatteRabbit)) + { + return false; + } + if (ignoreMaterialCheck) + { + return true; + } + return Bot.GetMonsters().Count(c => c.IsFaceup() && c.HasType(CardType.Effect) + && (!c.IsCode(CardId.SilhouhatteRabbit) || !summonThisTurn.Contains(c))) >= 2; + } + + public bool LinkSpiderSummon() + { + // check whether monster is enough + int monsterCount = Bot.GetMonsterCount() + Duel.MainPhase.ActivableCards.Count(c => c.IsOnField() && c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity)); + if (monsterCount < 2) + { + return false; + } + + // summon little knight check + if (Bot.ExtraDeck.Any(c => c.IsCode(CardId.SPLittleKnight))) + { + if (SPLittleKnightSummonCheck()) + { + return false; + } + if (SPLittleKnightSummonCheck(true)) + { + return true; + } + } + // summon silhouhatte rabbit check + if (Bot.ExtraDeck.Any(c => c.IsCode(CardId.SilhouhatteRabbit))) + { + if (SilhouhatteRabbitSummonCheck()) + { + return false; + } + if (SilhouhatteRabbitSummonCheck(true)) + { + return true; + } + } + return false; + } + + public bool MonsterRepos() + { + int selfAttack = Card.Attack + 1; + + if (selfAttack <= 1) + return !Card.IsDefense(); + + int bestAttack = 0; + foreach (ClientCard card in Bot.GetMonsters()) + { + int attack = card.Attack; + if (attack >= bestAttack) + { + bestAttack = attack; + } + } + + bool enemyBetter = Util.IsAllEnemyBetterThanValue(bestAttack, true); + + if (Card.IsAttack() && enemyBetter) + return true; + if (Card.IsDefense() && !enemyBetter) + return true; + return false; + } + + public bool MonstetReposForImportantMonsters() + { + if (!Card.IsFacedown()) + { + return false; + } + return Card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz); + } + + public bool SpellSetFirst() + { + // pretend to have solemn report + int setCount = Bot.GetSpells().Count(c => c.IsFacedown() && c.Sequence < 5); + if (setCount < 2 && Bot.LifePoints > 1500) + { + if (setCount == 1 && !Bot.HasInSpellZone(CardId.SolemnReport) && Bot.HasInHand(CardId.SolemnReport) && Bot.GetSpells().Any(c => c.IsTrap() && c.IsFacedown())) + { + if (!Card.IsCode(CardId.SolemnReport)) + { + return false; + } + SelectSTPlace(Card, true); + return true; + } + // if card should set + List canSetSpells = GetCanSetSpells(); + if (setCount + canSetSpells.Count() > 0 && canSetSpells.Contains(Card) && (!Bot.HasInHand(CardId.SolemnReport) || Card.IsTrap())) + { + bool avoidImpermanence = Card.IsCode(CardId.SolemnReport); + SelectSTPlace(Card, avoidImpermanence); + return true; + } + } + return false; + } + + public List GetCanSetSpells() + { + List canSetSpells = new List(); + foreach (ClientCard card in Bot.Hand) + { + bool setFlag = false; + switch (Card.Id) + { + case CardId.Terraforming: + setFlag |= CheckRemainInDeck(CardId.TreasuresOfTheKings) > 0 && DefaultCheckWhetherBotCanSearch(); + break; + case CardId.PrimiteLordlyLode: + setFlag |= PrimiteLordlyLodeActivateCheck() && !canSetSpells.Any(c => c.IsCode(CardId.PrimiteLordlyLode)); + break; + case CardId.DominusSpark: + setFlag |= !enemyActivateMonsterEffectFromHandGrave; + break; + case _CardId.InfiniteImpermanence: + case CardId.VerdictOfAnubis: + case CardId.SolemnReport: + setFlag = true; + break; + case CardId.DominusPurge: + case CardId.DominusImpulse: + setFlag |= Enemy.GetMonsterCount() + Enemy.GetSpellCount() == 0; + break; + case CardId.SongsOfTheDominators: + setFlag |= !Bot.Graveyard.Any(c => c.IsMonster()); + break; + case CardId.ApophisTheSwampDeity: + setFlag |= Bot.Hand.Any(c => c != card && c.HasType(CardType.Continuous) && c.HasType(CardType.Trap)); + setFlag |= Bot.GetSpells().Any(c => c != card && c.HasType(CardType.Continuous) && c.HasType(CardType.Trap)); + setFlag |= Bot.GetMonsters().Any(c => c != card && c.HasType(CardType.Continuous) && c.HasType(CardType.Trap)); + break; + case CardId.ApophisTheSerpent: + setFlag |= CheckRemainInDeck(CardId.ApophisTheSwampDeity) > 0; + setFlag |= Bot.HasInHandOrInSpellZone(CardId.ApophisTheSwampDeity); + break; + default: + setFlag = false; + break; + } + if (setFlag) + { + canSetSpells.Add(card); + } + } + return canSetSpells; + } + + public bool SpellSet() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + + // select place + if (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) + { + switch (Card.Id) + { + case _CardId.InfiniteImpermanence: + // do not set infinite impermanence if don't need to set other cards + if (Bot.GetMonsterCount() == 0 && Bot.GetSpellCount() == 0 + && !Bot.Hand.Any(c => !c.IsCode(_CardId.InfiniteImpermanence) && (c.IsTrap() || c.HasType(CardType.QuickPlay))) + && Bot.Hand.Count() <= 6) + { + return false; + } + break; + case CardId.SongsOfTheDominators: + // do not set songs of the dominators if don't need to set + if (!Bot.Graveyard.Any(c => c.IsMonster()) && Bot.GetMonsterCount() == 0) + { + return false; + } + if (Bot.GetSpells().Any(c => c.IsCode(CardId.SongsOfTheDominators) && c.IsFacedown())) + { + return false; + } + break; + case CardId.ApophisTheSwampDeity: + // do not set swamp deity if don't need to set + if ( + !Bot.Hand.Any(c => c != Card && c.HasType(CardType.Continuous) && c.HasType(CardType.Trap)) && + !Bot.GetSpells().Any(c => c != Card && c.HasType(CardType.Continuous) && c.HasType(CardType.Trap)) && + !Bot.GetMonsters().Any(c => c != Card && c.HasType(CardType.Continuous) && c.HasType(CardType.Trap)) && + !Bot.HasInHandOrInSpellZone(CardId.PrimiteDrillbeam) && + !(Bot.GetMonsterCount() == 0 + && !Bot.Hand.Any(c => c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity) && c != Card) + && !Bot.GetSpells().Any(c => c.IsCode(CardId.ApophisTheSerpent, CardId.ApophisTheSwampDeity) && c != Card && c.IsFacedown()) + ) + ) + { + return false; + } + break; + case CardId.SolemnReport: + { + if (Bot.LifePoints <= 1500) + { + return false; + } + break; + } + case CardId.ApophisTheSerpent: + case CardId.DominusImpulse: + case CardId.DominusPurge: + case CardId.DominusSpark: + if (Bot.GetSpells().Any(c => c.IsCode(Card.Id) && c.IsFacedown())) + { + return false; + } + break; + default: + break; + } + + List avoid_list = new List(); + int setForInfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setForInfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setForInfiniteImpermanence); + return true; + } + else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } + else + { + SelectSTPlace(); + } + return true; + } + + else if (Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true) || Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true)) + { + if (Card.IsSpell() && !Bot.HasInSpellZone(Card.Id)) + { + SelectSTPlace(); + return true; + } + } + + return false; + } + + } +} diff --git a/Game/AI/Decks/BlueEyesExecutor.cs b/Game/AI/Decks/BlueEyesExecutor.cs index 6cfa60b2..ecb72177 100644 --- a/Game/AI/Decks/BlueEyesExecutor.cs +++ b/Game/AI/Decks/BlueEyesExecutor.cs @@ -131,6 +131,7 @@ public override void OnNewTurn() UsedGalaxyEyesCipherDragon = null; AlternativeWhiteDragonSummoned = false; SoulChargeUsed = false; + base.OnNewTurn(); } public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) @@ -146,7 +147,8 @@ public override IList OnSelectCard(IList cards, int min, return Util.CheckSelectCount(result, cards, min, max); } Logger.DebugWriteLine("Use default."); - return null; + + return base.OnSelectCard(cards, min, max, hint, cancelable); } public override IList OnSelectXyzMaterial(IList cards, int min, int max) @@ -175,6 +177,22 @@ public override IList OnSelectSynchroMaterial(IList card return null; } + public override void OnSpSummoned() + { + // not special summoned by chain + if (Duel.GetCurrentSolvingChainCard() == null) + { + foreach (ClientCard card in Duel.LastSummonedCards) + { + if (card.Controller == 0 && card.IsCode(CardId.AlternativeWhiteDragon)) + { + AlternativeWhiteDragonSummoned = true; + } + } + } + base.OnSpSummoned(); + } + private bool DragonShrineEffect() { AI.SelectCard( @@ -499,7 +517,6 @@ private bool WhiteStoneOfAncientsEffect() private bool AlternativeWhiteDragonSummon() { - AlternativeWhiteDragonSummoned = true; return true; } diff --git a/Game/AI/Decks/BlueEyesMaxDragonExecutor.cs b/Game/AI/Decks/BlueEyesMaxDragonExecutor.cs index 5aa4ad0b..6b26c1aa 100644 --- a/Game/AI/Decks/BlueEyesMaxDragonExecutor.cs +++ b/Game/AI/Decks/BlueEyesMaxDragonExecutor.cs @@ -121,6 +121,7 @@ private void Count_check() private bool MaxxCeff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Duel.Player == 1; } @@ -464,6 +465,7 @@ private bool Linkuribohsp() private bool Linkuriboheff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Duel.LastChainPlayer == 0 && Util.GetLastChainCard().IsCode(CardId.Linkuriboh)) return false; return true; } diff --git a/Game/AI/Decks/BraveExecutor.cs b/Game/AI/Decks/BraveExecutor.cs index 718ed751..1fba1941 100644 --- a/Game/AI/Decks/BraveExecutor.cs +++ b/Game/AI/Decks/BraveExecutor.cs @@ -1,4 +1,4 @@ -using System; +using System; using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using WindBot; @@ -180,6 +180,7 @@ public override void OnNewTurn() FusionDestinyUsed = false; PhoenixTarget = null; PhoenixSelectingTarget = 0; + base.OnNewTurn(); } public override CardPosition OnSelectPosition(int cardId, IList positions) @@ -479,6 +480,7 @@ private bool JourneyOfDestinyEffect() private bool AquamancerOfTheSanctuarySearchEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Card.Location == CardLocation.Grave) { AI.SelectCard(CardLocation.Deck); @@ -626,6 +628,7 @@ private bool TGHyperLibrarianSummon() private bool JetSynchronEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; int[] materials = new[] { CardId.MechaPhantomBeastToken }; @@ -786,6 +789,7 @@ private bool PredaplantVerteAnacondaSummon() private bool PredaplantVerteAnacondaEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (ActivateDescription == Util.GetStringId(CardId.PredaplantVerteAnaconda, 0)) return false; FusionDestinyUsed = true; @@ -858,6 +862,7 @@ private bool DestinyHeroDasherEffect() private bool DestinyHeroCelestialEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (!Bot.HasInGraveyard(CardId.DestinyHeroDasher)) return false; AI.SelectCard(CardId.DestinyHeroDasher); @@ -971,6 +976,7 @@ private bool VirtualWorldKyubiShenshenSummon() private bool VirtualWorldKyubiShenshenEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Card.Location == CardLocation.MonsterZone && Bot.HasInBanished(CardId.AquamancerOfTheSanctuary)) { AI.SelectCard(CardId.AquamancerOfTheSanctuary); diff --git a/Game/AI/Decks/ChainBurnExecutor.cs b/Game/AI/Decks/ChainBurnExecutor.cs index 0708acd4..3d106268 100644 --- a/Game/AI/Decks/ChainBurnExecutor.cs +++ b/Game/AI/Decks/ChainBurnExecutor.cs @@ -265,6 +265,7 @@ public override void OnNewTurn() prevent_used = false; Linkuribohused = true; Timelord_check = false; + base.OnNewTurn(); } public override void OnNewPhase() { diff --git a/Game/AI/Decks/DarkMagicianExecutor.cs b/Game/AI/Decks/DarkMagicianExecutor.cs index 024b3153..0e988e4f 100644 --- a/Game/AI/Decks/DarkMagicianExecutor.cs +++ b/Game/AI/Decks/DarkMagicianExecutor.cs @@ -316,6 +316,7 @@ public override void OnNewTurn() big_attack = false; big_attack_used = false; soul_used = false; + base.OnNewTurn(); } public int GetTotalATK(IList list) { @@ -496,11 +497,13 @@ private bool SolemnStrikeeff() { if (Bot.LifePoints > 1500 && Duel.LastChainPlayer == 1) return true; + if (DefaultOnlyHorusSpSummoning()) return false; return false; } private bool ChainEnemy() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsCode(CardId.UpstartGoblin)) return false; @@ -519,6 +522,7 @@ private bool CrystalWingSynchroDragoneff() private bool MaxxCeff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Duel.Player == 1; } /* @@ -1417,6 +1421,7 @@ private bool MagiciansRodeff() } else { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Bot.HasInMonstersZone(CardId.VentriloauistsClaraAndLucika)) { AI.SelectCard(CardId.VentriloauistsClaraAndLucika); diff --git a/Game/AI/Decks/DogmatikaExecutor.cs b/Game/AI/Decks/DogmatikaExecutor.cs new file mode 100644 index 00000000..b222f79d --- /dev/null +++ b/Game/AI/Decks/DogmatikaExecutor.cs @@ -0,0 +1,2902 @@ +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using System; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Dogmatika", "AI_Dogmatika")] + + public class DogmatikaExecutor : DefaultExecutor + { + public class CardId + { + public const int DogmatikaAlbaZoa = 51522296; + public const int ThesIrisSwordsoul = 62849088; + public const int DogmatikaFleurdelis = 69680031; + public const int DogmatikaMaximus = 95679145; + public const int DiabellstarTheBlackWitch = 72270339; + public const int DogmatikaEcclesia = 60303688; + // _CardId.AshBlossom = 14558127; + // _CardId.MaxxC = 23434538; + public const int KnightmareCorruptorIblee = 10158145; + + public const int NadirServant = 1984618; + public const int DogmatikaLamity = 31002402; + public const int DogmatikaMacabre = 60921537; + public const int SinfulSpoilsOfDoom_Rciela = 16240772; + // _CardId.CalledByTheGrave = 24224830; + // _CardId.CrossoutDesignator = 65681983; + public const int WANTED_SeekerOfSinfulSpoils = 80845034; + public const int DogmatikaMatrix = 35569555; + + // _CardId.InfiniteImpermanence = 10045474; + public const int DogmatikaPunishment = 82956214; + + public const int GranguignolTheDuskDragon = 24915933; + public const int TitanikladTheAshDragon = 41373230; + public const int GaruraWingsOfResonantLife = 11765832; + public const int ElderEntityNtss = 80532587; + public const int DespianLuluwalilith = 53971455; + public const int PSYFramelordOmega = 74586817; + public const int HeraldOfTheArcLight = 79606837; + public const int SuperStarslayerTYPHON = 93039339; + public const int SPLittleKnight = 29301450; + public const int SecureGardna = 2220237; + public const int Linguriboh = 24842059; + public const int SalamangreatAlmiraj = 60303245; + + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + public const int SkillDrain = 82732705; + + public const int DimensionShifter = 91800273; + public const int MacroCosmos = 30241314; + public const int DimensionalFissure = 81674782; + public const int BanisheroftheRadiance = 94853057; + public const int BanisheroftheLight = 61528025; + public const int GhostMournerMoonlitChill = 52038441; + } + + public DogmatikaExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + // startup effect + AddExecutor(ExecutorType.Activate, CardId.WANTED_SeekerOfSinfulSpoils, WANTED_SeekerOfSinfulSpoilsActivate); + AddExecutor(ExecutorType.Activate, CardId.SalamangreatAlmiraj, SalamangreatAlmirajActivate); + + AddExecutor(ExecutorType.Activate, CardId.PSYFramelordOmega, PSYFramelordOmegaActivate); + AddExecutor(ExecutorType.Activate, CardId.DogmatikaAlbaZoa, DogmatikaAlbaZoaActivate); + AddExecutor(ExecutorType.Activate, CardId.DogmatikaMaximus, DogmatikaMaximusActivate); + AddExecutor(ExecutorType.Activate, CardId.DiabellstarTheBlackWitch, DiabellstarTheBlackWitchActivate); + + // quick effect + AddExecutor(ExecutorType.Activate, CardId.ThesIrisSwordsoul, ThesIrisSwordsoulActivate); + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, _CardId.CrossoutDesignator, CrossoutDesignatorActivate); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, CardId.Linguriboh, LinguribohActivate); + AddExecutor(ExecutorType.Activate, CardId.DogmatikaPunishment, DogmatikaPunishmentActivate); + AddExecutor(ExecutorType.Activate, CardId.DogmatikaFleurdelis, DogmatikaFleurdelisActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, CardId.SinfulSpoilsOfDoom_Rciela, SinfulSpoilsOfDoom_RcielaActivate); + AddExecutor(ExecutorType.Activate, ClearIrisFlag); + + AddExecutor(ExecutorType.Activate, CardId.HeraldOfTheArcLight, HeraldOfTheArcLightActivate); + AddExecutor(ExecutorType.Activate, CardId.ElderEntityNtss, ElderEntityNtssActivate); + AddExecutor(ExecutorType.Activate, CardId.GranguignolTheDuskDragon, GranguignolTheDuskDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.GaruraWingsOfResonantLife, GaruraWingsOfResonantLifeActivate); + AddExecutor(ExecutorType.Activate, CardId.KnightmareCorruptorIblee, KnightmareCorruptorIbleeActivate); + AddExecutor(ExecutorType.Activate, CardId.TitanikladTheAshDragon, TitanikladTheAshDragonActivate); + AddExecutor(ExecutorType.Activate, CardId.DespianLuluwalilith, DespianLuluwalilithActivate); + + // free chain + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + AddExecutor(ExecutorType.Activate, CardId.SuperStarslayerTYPHON, SuperStarslayerTYPHONActivate); + + // spsummon + AddExecutor(ExecutorType.SpSummon, CardId.Linguriboh, LinguribohSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SalamangreatAlmiraj, SalamangreatAlmirajSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SecureGardna, SecureGardnaSpSummon); + + // startup effect + AddExecutor(ExecutorType.Summon, CardId.KnightmareCorruptorIblee, KnightmareCorruptorIbleeSummon); + AddExecutor(ExecutorType.Activate, CardId.NadirServant, NadirServantActivate); + + // summon + AddExecutor(ExecutorType.Summon, CardId.DogmatikaEcclesia, DogmatikaEcclesiaSummon); + AddExecutor(ExecutorType.Activate, CardId.DogmatikaEcclesia, DogmatikaEcclesiaActivate); + + // ritual + AddExecutor(ExecutorType.Activate, CardId.DogmatikaMatrix, DogmatikaMatrixActivate); + AddExecutor(ExecutorType.Activate, CardId.DogmatikaLamity, DogmatikaLamityActivate); + AddExecutor(ExecutorType.Activate, CardId.DogmatikaMacabre, DogmatikaMacabreActivate); + + // other + AddExecutor(ExecutorType.Activate, CardId.DogmatikaFleurdelis, DogmatikaFleurdelisDelayActivate); + AddExecutor(ExecutorType.Repos, MonsterRepos); + AddExecutor(ExecutorType.Summon, SummonForTYPHONCheck); + AddExecutor(ExecutorType.SpSummon, CardId.DiabellstarTheBlackWitch, DiabellstarTheBlackWitchSpSummon); + AddExecutor(ExecutorType.Activate, CardId.DogmatikaLamity, DogmatikaLamityDelayActivate); + AddExecutor(ExecutorType.SpSummon, CardId.SuperStarslayerTYPHON, SuperStarslayerTYPHONSpSummon); + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + } + + const int SetcodeTimeLord = 0x4a; + const int SetcodePhantom = 0xdb; + const int SetcodeOrcust = 0x11b; + const int SetcodeDogmatika = 0x146; + const int hintTimingMainEnd = 0x4; + const int hintDamageStep = 0x2000; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.DogmatikaEcclesia, _CardId.AshBlossom, _CardId.MaxxC, CardId.KnightmareCorruptorIblee, CardId.NadirServant, + CardId.WANTED_SeekerOfSinfulSpoils, CardId.DogmatikaMatrix, _CardId.InfiniteImpermanence, CardId.DogmatikaPunishment }}, + {2, new List { CardId.DogmatikaAlbaZoa, CardId.DogmatikaFleurdelis, _CardId.CalledByTheGrave }}, + {1, new List { CardId.ThesIrisSwordsoul, CardId.DogmatikaMaximus, CardId.DiabellstarTheBlackWitch, CardId.DogmatikaLamity, CardId.DogmatikaMacabre, + CardId.SinfulSpoilsOfDoom_Rciela, _CardId.CrossoutDesignator }}, + }; + List notToNegateIdList = new List{ + 58699500, 20343502 + }; + List discardEnemyExtraIdList = new List{ + _CardId.DivineArsenalAAZEUS_SkyThunder, CardId.SPLittleKnight, CardId.Number41BagooskatheTerriblyTiredTapir, + 70534340, 60465049, 24094258, 86066372 + }; + + List currentNegatingIdList = new List(); + bool enemyActivateMaxxC = false; + bool enemyActivateLockBird = false; + List infiniteImpermanenceList = new List(); + bool summoned = false; + List activatedCardIdList = new List(); + List currentNegateMonsterList = new List(); + List currentDestroyCardList = new List(); + List discardExtraThisTurn = new List(); + int banSpSummonFromExTurn = 0; + List activatedMatrixList = new List(); + List maximusDiscardExtraIdList = new List(); + bool checkedEnemyExtra = false; + bool matrixActivating = false; + bool avoid2Monster = true; + bool confirmLink2 = false; + int omegaActivateCount = 0; + int dimensionShifterCount = 0; + + int enemySpSummonFromExLastTurn = 0; + int enemySpSummonFromExThisTurn = 0; + bool enemySpSummonFromDeck = false; + bool enemySpSummonFromExtra = false; + + /// + /// Shuffle List and return a random-order card list + /// + public List ShuffleCardList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + ClientCard tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } + + public void UpdateBanSpSummonFromExTurn(int newTurn) + { + if (Duel.Player == 1) newTurn -= 1; + banSpSummonFromExTurn = Math.Max(banSpSummonFromExTurn, newTurn); + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = false) + { + List floodagateList = Enemy.GetMonsters().Where(c => c?.Data != null && + c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget()) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).ToList(); + if (floodagateList.Count() > 0) + { + floodagateList.Sort(CardContainer.CompareCardAttack); + floodagateList.Reverse(); + return floodagateList[0]; + } + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterDangerous() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget()) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).ToList(); + if (dangerList.Count() > 0) + { + dangerList.Sort(CardContainer.CompareCardAttack); + dangerList.Reverse(); + return dangerList[0]; + } + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterInvincible() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget()) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).ToList(); + if (invincibleList.Count() > 0) + { + invincibleList.Sort(CardContainer.CompareCardAttack); + invincibleList.Reverse(); + return invincibleList[0]; + } + + if (attack >= 0) + { + if (attack == 0) + attack = Util.GetBestAttack(Bot); + List betterList = Enemy.MonsterZone.GetMonsters() + .Where(card => card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && (!canBeTarget || !card.IsShouldNotBeTarget()) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList(); + if (betterList.Count() > 0) + { + betterList.Sort(CardContainer.CompareCardAttack); + betterList.Reverse(); + return betterList[0]; + } + } + return null; + } + + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreNormalSpell = false) + { + List resultList = new List(); + + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (floodagateList.Count() > 0) + { + floodagateList.Sort(CardContainer.CompareCardAttack); + floodagateList.Reverse(); + resultList.AddRange(floodagateList); + } + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count() > 0) + { + resultList.AddRange(ShuffleCardList(problemEnemySpellList)); + } + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (dangerList.Count() > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) + { + dangerList.Sort(CardContainer.CompareCardAttack); + dangerList.Reverse(); + resultList.AddRange(dangerList); + } + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (invincibleList.Count() > 0) + { + invincibleList.Sort(CardContainer.CompareCardAttack); + invincibleList.Reverse(); + resultList.AddRange(invincibleList); + } + + List enemyMonsters = Enemy.GetMonsters().ToList(); + if (enemyMonsters.Count() > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach(ClientCard target in enemyMonsters) + { + if (target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2) ) + { + if (!canBeTarget || !(target.IsShouldNotBeTarget() || target.IsShouldNotBeMonsterTarget())) + { + if (!resultList.Contains(target)) + { + resultList.Add(target); + } + } + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous)).ToList(); + if (spells.Count() > 0 && !ignoreNormalSpell) + { + resultList.AddRange(ShuffleCardList(spells)); + } + + return resultList; + } + + public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false, bool ignoreCurrentDestroy = false) + { + ClientCard card = GetProblematicEnemyMonster(0, canBeTarget, ignoreCurrentDestroy); + if (card != null) + return card; + + card = Enemy.MonsterZone.Where(c => c?.Data != null && c.HasType(CardType.Monster) && c.IsFaceup() + && !(canBeTarget && c.IsShouldNotBeTarget()) && (!ignoreCurrentDestroy || currentDestroyCardList.Contains(c))) + .OrderByDescending(c => c.Attack).FirstOrDefault(); + if (card != null) + return card; + + List monsters = Enemy.GetMonsters().Where(c => !ignoreCurrentDestroy || currentDestroyCardList.Contains(c)).ToList(); + + // after GetHighestAttackMonster, the left monsters must be face-down. + if (monsters.Count() > 0 && !onlyFaceup) + return ShuffleCardList(monsters)[0]; + + return null; + } + + public ClientCard GetBestEnemySpell(bool onlyFaceup = false, bool canBeTarget = false) + { + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count() > 0) + { + return ShuffleCardList(problemEnemySpellList)[0]; + } + + List spells = Enemy.GetSpells().Where(card => !(card.IsFaceup() && card.IsCode(_CardId.EvenlyMatched))).ToList(); + + List faceUpList = spells.Where(ecard => ecard.IsFaceup() && + ecard.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous)).ToList(); + if (faceUpList.Count() > 0) + { + return ShuffleCardList(faceUpList)[0]; + } + + if (spells.Count() > 0 && !onlyFaceup) + { + return ShuffleCardList(spells)[0]; + } + + return null; + } + + public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false, bool checkGrave = false) + { + ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget); + if (card != null) + { + return card; + } + + card = GetBestEnemySpell(onlyFaceup, canBeTarget); + if (card != null) + { + return card; + } + + if (checkGrave && Enemy.Graveyard.Count() > 0) + { + List graveMonsterList = Enemy.Graveyard.GetMatchingCards(c => c.IsMonster()).ToList(); + if (graveMonsterList.Count() > 0) + { + graveMonsterList.Sort(CardContainer.CompareCardAttack); + graveMonsterList.Reverse(); + return graveMonsterList[0]; + } + return ShuffleCardList(Enemy.Graveyard.ToList())[0]; + } + + return null; + } + + /// + /// check enemy's dangerous card in grave + /// + public List GetDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantom))).ToList(); + List dangerMonsterIdList = new List{ + 99937011, 63542003, 9411399, 28954097, 30680659, CardId.PSYFramelordOmega + }; + result.AddRange(Enemy.Graveyard.GetMatchingCards(card => dangerMonsterIdList.Contains(card.Id))); + return result; + } + + public List GetNormalEnemyTargetList(bool canBeTarget = true, bool targetKnightmare = true, bool ignoreCurrentDestroy = false) + { + List targetList = GetProblematicEnemyCardList(canBeTarget); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && !card.IsCode(CardId.KnightmareCorruptorIblee) && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleCardList(Enemy.GetSpells().Where(card => !ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)).ToList())); + targetList.AddRange(ShuffleCardList(Enemy.GetMonsters().Where(card => card.IsFacedown() && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList())); + if (targetKnightmare) + { + List enemyKnightmare = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) && card.IsCode(CardId.KnightmareCorruptorIblee) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList(); + targetList.AddRange(enemyKnightmare); + } + + return targetList; + } + + public List GetMonsterListForTargetNegate(bool canBeMonsterTarget = false, bool canBeTrapTarget = false) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; + } + + // negate before used + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && (!canBeMonsterTarget || !card.IsShouldNotBeMonsterTarget()) && (!canBeTrapTarget || !card.IsShouldNotBeSpellTrapTarget()) + && !currentNegateMonsterList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + // negate monster effect on the field + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && (!canBeMonsterTarget || !chainingCard.IsShouldNotBeMonsterTarget()) && (!canBeTrapTarget || !chainingCard.IsShouldNotBeSpellTrapTarget()) + && !chainingCard.IsShouldNotBeTarget() && !currentNegateMonsterList.Contains(chainingCard)) + { + resultList.Add(chainingCard); + } + } + + return resultList; + } + + /// + /// Get ritual monster/spell's ids that need to search, in order to perform ritual summon. + /// + public List GetNeedSearchRitualCardIdList() + { + List result = new List(); + + bool canSearchAlbaZoa = !Bot.HasInHand(CardId.DogmatikaAlbaZoa) && CheckRemainInDeck(CardId.DogmatikaAlbaZoa) > 0; + int totalLevelInGY = Bot.Graveyard.Where(card => card != null && card.HasType(CardType.Fusion | CardType.Synchro)).Sum(c => (int?)c.Level ?? 0); + + bool needSearchAlbaZoa = Bot.HasInHandOrInSpellZone(CardId.DogmatikaLamity) && Bot.HasInExtra(CardId.DespianLuluwalilith) && canSearchAlbaZoa; + if (Bot.HasInHandOrInGraveyard(CardId.DogmatikaMacabre)) + { + needSearchAlbaZoa |= totalLevelInGY >= 12 && canSearchAlbaZoa; + } + if (needSearchAlbaZoa) + { + result.Add(CardId.DogmatikaAlbaZoa); + } + + if (Bot.HasInHand(CardId.DogmatikaAlbaZoa) && Bot.HasInExtra(CardId.DespianLuluwalilith) && !Bot.HasInHandOrInSpellZone(CardId.DogmatikaLamity) + && CheckRemainInDeck(CardId.DogmatikaLamity) > 0) + { + result.Add(CardId.DogmatikaLamity); + } + + if (Bot.HasInHand(CardId.DogmatikaAlbaZoa) && !Bot.HasInHandOrInSpellZone(CardId.DogmatikaMacabre) && CheckRemainInDeck(CardId.DogmatikaMacabre) > 0 + && totalLevelInGY >= 12) + { + result.Add(CardId.DogmatikaMacabre); + } + + return result; + } + + public ClientCard GetExtraToDiscard(int baseAtk = 0, ClientCard avoidDestroyEnemyCard = null) + { + ClientCard selectResult = null; + // Ntss + if (baseAtk <= 2500 && Bot.HasInExtra(CardId.ElderEntityNtss) && CheckCalledbytheGrave(CardId.ElderEntityNtss) == 0) + { + List destroyList = GetNormalEnemyTargetList(true, false); + if (destroyList.Count() > 0) + { + if (!(destroyList.Count() == 1 && destroyList[0] == avoidDestroyEnemyCard)) + { + selectResult = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ElderEntityNtss)); + if (selectResult != null) + { + return selectResult; + } + } + } + } + + // Garura + if (baseAtk <= 1500 && Bot.HasInExtra(CardId.GaruraWingsOfResonantLife) && CheckCalledbytheGrave(CardId.GaruraWingsOfResonantLife) == 0 + && !activatedCardIdList.Contains(CardId.GaruraWingsOfResonantLife) && !enemyActivateLockBird) + { + selectResult = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.GaruraWingsOfResonantLife)); + if (selectResult != null) + { + return selectResult; + } + } + + // Ash Dragon + if (baseAtk <= 2500 && Bot.HasInExtra(CardId.TitanikladTheAshDragon) && CheckCalledbytheGrave(CardId.TitanikladTheAshDragon) == 0 + && !discardExtraThisTurn.Contains(CardId.TitanikladTheAshDragon) && !enemyActivateLockBird) + { + bool successFlag = !activatedCardIdList.Contains(CardId.DogmatikaEcclesia) && CheckRemainInDeck(CardId.DogmatikaEcclesia) > 0; + successFlag |= Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika)) && !Bot.HasInHand(CardId.DogmatikaFleurdelis) && CheckRemainInDeck(CardId.DogmatikaFleurdelis) > 0; + if (successFlag) + { + selectResult = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.TitanikladTheAshDragon)); + if (selectResult != null) + { + return selectResult; + } + } + } + + // dusk dragon + if (baseAtk <= 2500 && Bot.HasInExtra(CardId.GranguignolTheDuskDragon)) + { + bool successFlag = Bot.HasInExtra(CardId.DespianLuluwalilith); + successFlag |= CheckRemainInDeck(CardId.DogmatikaEcclesia, CardId.DogmatikaFleurdelis, CardId.DogmatikaMaximus) > 0; + if (successFlag) + { + selectResult = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.GranguignolTheDuskDragon)); + if (selectResult != null) + { + return selectResult; + } + } + } + + if (baseAtk <= 600 && Bot.HasInExtra(CardId.HeraldOfTheArcLight) && !enemyActivateLockBird) + { + if (GetNeedSearchRitualCardIdList().Count() > 0) + { + selectResult = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.HeraldOfTheArcLight)); + if (selectResult != null) + { + return selectResult; + } + } + } + + if (baseAtk <= 2800 && Bot.HasInExtra(CardId.PSYFramelordOmega)) + { + selectResult = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.PSYFramelordOmega)); + if (selectResult != null) + { + return selectResult; + } + } + + List discardableList = Bot.ExtraDeck.Where(card => card != null && card.Attack >= baseAtk).ToList(); + if (discardableList.Count() > 0) + { + discardableList.Sort(CardContainer.CompareCardAttack); + return discardableList[0]; + } + + return selectResult; + } + + /// + /// Check whether negate opposite's effect and clear flag + /// + public void CheckDeactiveFlag() + { + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && Duel.LastChainPlayer == 1) + { + if (lastChainCard.IsCode(_CardId.MaxxC)) enemyActivateMaxxC = false; + if (lastChainCard.IsCode(_CardId.LockBird)) enemyActivateLockBird = false; + if (lastChainCard.IsCode(CardId.DimensionShifter)) dimensionShifterCount = 0; + if (lastChainCard.Controller == 1 && lastChainCard.Location == CardLocation.MonsterZone) + { + currentNegateMonsterList.Add(lastChainCard); + } + } + } + + /// + /// Check negated turn count of id + /// + public int CheckCalledbytheGrave(int id) + { + if (currentNegatingIdList.Contains(id)) return 1; + if (DefaultCheckWhetherCardIdIsNegated(id)) return 1; + return 0; + } + + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + + public int CheckRemainInDeck(params int[] ids) + { + int sumResult = 0; + foreach (int id in ids) + { + sumResult += CheckRemainInDeck(id); + } + + return sumResult; + } + + /// + /// Whether spell or trap will be negate. If so, return true. + /// + /// is counter trap + /// check target + /// + public bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + // target default set + if (target == null) target = Card; + // won't negate if not on field + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + // negate judge + if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap()) + { + if (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true)) return true; + } + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) { + return true; + } + } + // how to get here? + return false; + } + + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated(bool toFieldCheck = false) + { + if ((Card.IsSpell() || Card.IsTrap()) && CheckSpellWillBeNegate()){ + return true; + } + if (DefaultCheckWhetherCardIsNegated(Card)) + { + return true; + } + if (Card.IsMonster() && (toFieldCheck || Card.Location == CardLocation.MonsterZone)) + { + if (toFieldCheck || Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card)) || Bot.MonsterZone.Any(card => CheckNumber41(card))) + { + return true; + } + } + if (Enemy.HasInSpellZone(CardId.SkillDrain, true, true)) + { + return true; + } + } + return false; + } + + public bool CheckNumber41(ClientCard card) + { + return card != null && card.IsFaceup() && card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled(); + } + + /// + /// Check whether cards will be removed. If so, do not send cards to grave. + /// + public bool CheckWhetherWillbeRemoved() + { + if (dimensionShifterCount > 0) return true; + List checkIdList = new List { CardId.BanisheroftheRadiance, CardId.BanisheroftheLight, CardId.MacroCosmos, CardId.DimensionalFissure }; + foreach (int cardid in checkIdList) + { + List fields = new List { Bot, Enemy }; + foreach (ClientField cf in fields) + { + if (cf.HasInMonstersZone(cardid, true) || cf.HasInSpellZone(cardid, true)) + { + return true; + } + } + } + return false; + } + + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && + (!Bot.GetMonsters().Any(card => card.IsFaceup() && !card.IsCode(CardId.KnightmareCorruptorIblee)) || (Duel.Player == 0 && Duel.Turn == 1))) + { + return true; + } + return false; + } + + public bool CheckShouldNoMoreSpSummon() + { + if (CheckAtAdvantage() && enemyActivateMaxxC && Util.IsTurn1OrMain2()) + { + bool successFlag = false; + successFlag |= Bot.HasInHandOrInSpellZone(CardId.DogmatikaPunishment); + successFlag |= Bot.GetMonsters().Any(card => card.IsFaceup() && card.Level >= 7 && card.HasRace(CardRace.SpellCaster)); + successFlag |= Bot.HasInHand(CardId.DogmatikaFleurdelis) + && Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika)); + return successFlag; + } + return false; + } + + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (notToNegateIdList.Contains(lastcard.Id)) return false; + if (lastcard.IsCode(_CardId.LockBird)) + { + bool needToSearch = false; + List searchEffectIdList = new List{ CardId.DogmatikaEcclesia, CardId.NadirServant }; + foreach (int checkId in searchEffectIdList) + { + if (Bot.HasInHandOrInSpellZone(checkId) && !activatedCardIdList.Contains(checkId)) needToSearch = true; + } + if (discardExtraThisTurn.Contains(CardId.TitanikladTheAshDragon)) needToSearch = true; + + if (!needToSearch) return false; + } + + return true; + } + + public bool CheckHasExtraOnField(ClientCard exceptCard = null) + { + List fieldMonsterList = Bot.GetMonsters(); + fieldMonsterList.AddRange(Enemy.GetMonsters()); + bool hasExtraOnField = fieldMonsterList.Any(card => card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && card != exceptCard); + return hasExtraOnField; + } + + /// + /// go first + /// + public override bool OnSelectHand() + { + return true; + } + + public override int OnSelectOption(IList options) + { + // override for iris + List checkOptionList = new List{Util.GetStringId(CardId.ThesIrisSwordsoul, 4), Util.GetStringId(CardId.ThesIrisSwordsoul, 2)}; + foreach (int checkOption in checkOptionList) + { + for (int i = 0; i < options.Count(); ++ i) + { + if (options[i] == checkOption) return i; + } + } + return base.OnSelectOption(options); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2) + { + bool turnDefense = false; + if (cardData.Attack <= cardData.Defense) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (!cardData.HasType(CardType.Ritual) || cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } else if (cardData.HasType(CardType.Ritual)) + { + return CardPosition.FaceUpAttack; + } + int cardAttack = cardData.Attack; + if (cardId == CardId.DogmatikaFleurdelis && !activatedCardIdList.Contains(cardId + 1) && Duel.Player == 0) cardAttack += 500; + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardAttack); + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + if (Util.ChainContainPlayer(1) && hint == HintMsg.Remove && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + int botCount = Bot.GetMonsterCount() + Bot.GetSpellCount(); + int oppositeCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); + if (botCount - oppositeCount == min && min == max) + { + Logger.DebugWriteLine("=== Evenly Matched activated."); + List banishList = new List(); + List botMonsters = Bot.GetMonsters().Where(card => !card.HasType(CardType.Token)).ToList(); + + // non-ritual monster + List faceDownMonsters = botMonsters.Where(card => card.IsFacedown()).ToList(); + banishList.AddRange(faceDownMonsters); + List nonSynchroMonsters = botMonsters.Where(card => !card.HasType(CardType.Ritual) && !banishList.Contains(card)).ToList(); + nonSynchroMonsters.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(nonSynchroMonsters); + + // spells + List spells = Bot.GetSpells(); + banishList.AddRange(ShuffleCardList(spells)); + + // ritual monster + List synchroMonsters = botMonsters.Where(card => card.HasType(CardType.Ritual) && !banishList.Contains(card)).ToList(); + synchroMonsters.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(synchroMonsters); + + return Util.CheckSelectCount(banishList, cards, min, max); + } + } + if (maximusDiscardExtraIdList.Count() > 0 && min == 1 && max == 1 && hint == HintMsg.ToGrave) + { + List discardList = new List(); + foreach (int checkId in maximusDiscardExtraIdList) + { + ClientCard discardTarget = cards.FirstOrDefault(card => card.IsCode(checkId)); + if (discardTarget != null) discardList.Add(discardTarget); + } + if (discardList.Count() >= max) + { + if (discardList.Count() > 0) discardExtraThisTurn.Add(discardList[0]?.Id ?? 0); + return Util.CheckSelectCount(discardList, cards, min, max); + } + } + if (matrixActivating && hint == HintMsg.ToGrave && min == 1 && max == 1) + { + bool extraFlag = true; + // 0=not yet, 1=only bot, 2=only enemy, 3=mixed & skip + int enemyFlag = 0; + foreach (ClientCard card in cards) + { + extraFlag &= card.Location == CardLocation.Extra; + if (enemyFlag == 0) enemyFlag = card.Controller + 1; + else if (enemyFlag < 3 && enemyFlag != card.Controller + 1) enemyFlag = 3; + } + Logger.DebugWriteLine("===Matrix: extraFlag = " + extraFlag.ToString() + ", enemyFlag = " + enemyFlag.ToString()); + if (extraFlag && enemyFlag < 3) + { + List discardList = new List(); + // discard bot's extra + if (enemyFlag == 1) + { + ClientCard elder = null; + ClientCard ashDragon = null; + ClientCard garura = null; + ClientCard arcLight = null; + ClientCard psy = null; + ClientCard duskDragon = null; + ClientCard lilith = null; + foreach (ClientCard card in cards) + { + if (card.Id == CardId.ElderEntityNtss) elder = card; + if (card.Id == CardId.TitanikladTheAshDragon) ashDragon = card; + if (card.Id == CardId.GaruraWingsOfResonantLife) garura = card; + if (card.Id == CardId.HeraldOfTheArcLight) arcLight = card; + if (card.Id == CardId.PSYFramelordOmega) psy = card; + if (card.Id == CardId.GranguignolTheDuskDragon) duskDragon = card; + if (card.Id == CardId.DespianLuluwalilith) lilith = card; + } + + List destroyList = GetNormalEnemyTargetList(true, true, true); + if (elder != null && destroyList.Count() > 0) discardList.Add(elder); + if (ashDragon != null && !activatedCardIdList.Contains(CardId.TitanikladTheAshDragon) && !discardEnemyExtraIdList.Contains(CardId.TitanikladTheAshDragon)) + { + bool checkFlag = !activatedCardIdList.Contains(CardId.DogmatikaEcclesia) && CheckRemainInDeck(CardId.DogmatikaEcclesia) > 0 + && CheckCalledbytheGrave(CardId.DogmatikaEcclesia) == 0; + checkFlag |= CheckRemainInDeck(CardId.DogmatikaFleurdelis) > 0 && !Bot.HasInHand(CardId.DogmatikaFleurdelis) && !enemyActivateLockBird; + if (checkFlag) discardList.Add(ashDragon); + } + if (garura != null && !activatedCardIdList.Contains(CardId.GaruraWingsOfResonantLife) && !enemyActivateLockBird) discardList.Add(garura); + if (arcLight != null && GetNeedSearchRitualCardIdList().Count() > 0) discardList.Add(arcLight); + if (psy != null) discardList.Add(psy); + if (duskDragon != null) discardList.Add(duskDragon); + if (lilith != null && !activatedCardIdList.Contains(CardId.DespianLuluwalilith) && !discardEnemyExtraIdList.Contains(CardId.DespianLuluwalilith)) + { + discardList.Add(lilith); + } + + if (discardList.Count() > 0) discardExtraThisTurn.Add(discardList[0]?.Id ?? 0); + } + // discard enemy's extra + if (enemyFlag == 2) + { + checkedEnemyExtra = true; + avoid2Monster = false; + confirmLink2 = false; + List discardIfKnightmare = new List{ 96380700, 48068378, 14812471, 32995276, 30342076, CardId.Linguriboh, 3679218 }; + foreach (ClientCard card in cards) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(card.Id); + if (cardData != null) + { + confirmLink2 |= cardData.HasType(CardType.Link) && cardData.Level <= 2; + avoid2Monster |= (cardData.HasType(CardType.Link) && cardData.Level <= 2) || cardData.HasType(CardType.Synchro | CardType.Xyz); + if (Enemy.HasInMonstersZone(CardId.KnightmareCorruptorIblee)) + { + if (discardIfKnightmare.Contains(card.Id)) discardList.Add(card); + } + } + } + discardList = ShuffleCardList(discardList); + + // avoid link summon + foreach (ClientCard card in cards) + { + if (discardList.Contains(card)) continue; + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(card.Id); + if (cardData != null && Enemy.HasInMonstersZone(CardId.KnightmareCorruptorIblee) + && cardData.HasType(CardType.Link) && cardData.Level <= Enemy.GetMonsterCount()) + { + discardList.Add(card); + } + } + + // discard important card + foreach (ClientCard card in cards) + { + if (discardList.Contains(card)) continue; + if (discardEnemyExtraIdList.Contains(card.Id)) discardList.Add(card); + } + + // discard single card first + List singleCardList = new List(); + List multiCardList = new List(); + foreach (ClientCard card in cards) + { + if (discardList.Contains(card)) continue; + if (cards.Any(oc => card != oc && card.IsCode(oc.Id))) multiCardList.Add(card); + else singleCardList.Add(card); + } + + discardList.AddRange(singleCardList.OrderByDescending(c => { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(c.Id); + return cardData?.Attack ?? 0; + })); + discardList.AddRange(multiCardList.OrderByDescending(c => { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(c.Id); + return cardData?.Attack ?? 0; + })); + } + + if (discardList.Count() > 0) return Util.CheckSelectCount(discardList, cards, min, max); + } + } + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + public override void OnNewTurn() + { + if (Duel.Turn <= 1) + { + banSpSummonFromExTurn = 0; + checkedEnemyExtra = false; + avoid2Monster = true; + dimensionShifterCount = 0; + + enemySpSummonFromExLastTurn = 0; + enemySpSummonFromExThisTurn = 0; + } + enemyActivateMaxxC = false; + enemyActivateLockBird = false; + omegaActivateCount = 0; + enemySpSummonFromExLastTurn = enemySpSummonFromExThisTurn; + enemySpSummonFromExThisTurn = 0; + currentNegatingIdList.Clear(); + + if (dimensionShifterCount > 0) dimensionShifterCount--; + infiniteImpermanenceList.Clear(); + + summoned = false; + activatedCardIdList.Clear(); + discardExtraThisTurn.Clear(); + activatedMatrixList.Clear(); + if (Duel.Player == 1 && banSpSummonFromExTurn > 0) + { + banSpSummonFromExTurn -= 1; + } + base.OnNewTurn(); + } + + public override void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + if (previousControler == 1 && currentLocation == (int)CardLocation.MonsterZone) + { + if (previousLocation == (int)CardLocation.Deck) enemySpSummonFromDeck = true; + if (previousLocation == (int)CardLocation.Extra) + { + enemySpSummonFromExtra = true; + enemySpSummonFromExThisTurn ++; + } + } + + base.OnMove(card, previousControler, previousLocation, currentControler, currentLocation); + } + + public override BattlePhaseAction OnBattle(IList attackers, IList defenders) + { + if (attackers.Count() == 1 && defenders.Count() == 1) + { + if (defenders[0].IsCode(CardId.KnightmareCorruptorIblee) && !confirmLink2) return new BattlePhaseAction(BattlePhaseAction.BattleAction.ToMainPhaseTwo); + } + if (attackers.Count() > 0 && defenders.Count() > 0) + { + List sortedAttacker = attackers.OrderBy(card => card.Attack).ToList(); + for (int k = 0; k < sortedAttacker.Count; ++k) + { + ClientCard attacker = sortedAttacker[k]; + attacker.IsLastAttacker = k == sortedAttacker.Count - 1; + BattlePhaseAction result = OnSelectAttackTarget(attacker, defenders); + if (result != null) + return result; + } + } + + return base.OnBattle(attackers, defenders); + } + + public override BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders) + { + foreach (ClientCard defender in defenders) + { + attacker.RealPower = attacker.Attack; + defender.RealPower = defender.GetDefensePower(); + if (!OnPreBattleBetween(attacker, defender)) + continue; + + if (attacker.RealPower > defender.RealPower) + return AI.Attack(attacker, defender); + + if (attacker.RealPower == defender.RealPower && defender.IsAttack() && Bot.GetMonsterCount() >= Enemy.GetMonsterCount()) + return AI.Attack(attacker, defender); + } + + if (attacker.CanDirectAttack) + return AI.Attack(attacker, null); + + return null; + } + + public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) + { + if (!defender.IsMonsterHasPreventActivationEffectInBattle()) + { + if (!activatedCardIdList.Contains(CardId.DogmatikaFleurdelis + 1) && Bot.HasInMonstersZone(CardId.DogmatikaFleurdelis, true, false, true) + && attacker.HasSetcode(SetcodeDogmatika)) + { + attacker.RealPower += 500; + } + } + return base.OnPreBattleBetween(attacker, defender); + } + + public override void OnChaining(int player, ClientCard card) + { + if (card == null) return; + + if (player == 1) + { + if (card.IsCode(_CardId.InfiniteImpermanence) && !DefaultCheckWhetherCardIdIsNegated(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + infiniteImpermanenceList.Add(4-i); + break; + } + } + } + } + base.OnChaining(player, card); + } + + public override void OnChainSolved(int chainIndex) + { + ChainInfo currentCard = Duel.GetCurrentSolvingChainInfo(); + if (currentCard != null && !Duel.IsCurrentSolvingChainNegated() && currentCard.ActivatePlayer == 1) + { + if (currentCard.IsCode(_CardId.MaxxC)) + enemyActivateMaxxC = true; + if (currentCard.IsCode(_CardId.LockBird)) + enemyActivateLockBird = true; + if (currentCard.IsCode(CardId.DimensionShifter)) + dimensionShifterCount = 2; + if (currentCard.IsCode(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == currentCard.RelatedCard) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + } + } + + public override void OnChainEnd() + { + currentNegateMonsterList.Clear(); + currentDestroyCardList.Clear(); + maximusDiscardExtraIdList.Clear(); + matrixActivating = false; + base.OnChainEnd(); + } + + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public bool DogmatikaAlbaZoaActivate() + { + if (CheckWhetherNegated()) return false; + if (Enemy.GetMonsters().Any(card => card != null && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)) + && Duel.Phase == DuelPhase.Main1 && Enemy.ExtraDeck.Count() > 1) + { + return false; + } + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool ThesIrisSwordsoulActivate() + { + if (CheckWhetherNegated()) return false; + if (Card.Location == CardLocation.Hand) + { + if (CheckShouldNoMoreSpSummon()) + { + return false; + } + return true; + } + if (enemySpSummonFromDeck || enemySpSummonFromExtra) return true; + + return false; + } + + public bool ClearIrisFlag() + { + enemySpSummonFromDeck = false; + enemySpSummonFromExtra = false; + + return false; + } + + public bool DogmatikaFleurdelisActivate() + { + if (CheckWhetherNegated()) return false; + if (Card.Location == CardLocation.Hand) + { + bool canNegate = Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika)) && Enemy.GetMonsters().Any(card => card.IsFaceup()); + if (canNegate) + { + // for negate effect + List shouldNegateList = GetMonsterListForTargetNegate(true); + if (shouldNegateList.Count() > 0) + { + ClientCard target = shouldNegateList[0]; + currentNegateMonsterList.Add(target); + AI.SelectYesNo(true); + AI.SelectCard(target); + activatedCardIdList.Add(CardId.DogmatikaFleurdelis); + return true; + } + // for iris + if (Bot.HasInHand(CardId.ThesIrisSwordsoul)) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + if (target != null) + { + AI.SelectYesNo(true); + AI.SelectCard(target); + } else { + List enemyTargetList = ShuffleCardList(Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsDisabled()).ToList()); + if (enemyTargetList.Count() > 0) + { + AI.SelectYesNo(true); + AI.SelectCard(enemyTargetList); + } else AI.SelectYesNo(false); + } + activatedCardIdList.Add(CardId.DogmatikaFleurdelis); + return true; + } + } + // for total attack + if (Duel.Player == 0 && Enemy.GetMonsterCount() == 0) { + int totalAttack = Util.GetTotalAttackingMonsterAttack(0); + if (totalAttack < Enemy.LifePoints) + { + totalAttack += Bot.GetMonsters().Where(card => card.HasSetcode(SetcodeDogmatika)).Count() * 500 + 3000; + if (totalAttack >= Enemy.LifePoints) + { + activatedCardIdList.Add(CardId.DogmatikaFleurdelis); + AI.SelectYesNo(false); + return true; + } + } + } + // for avoid lose + if (Duel.Player == 1 && Bot.GetMonsterCount() == 0 && Util.GetTotalAttackingMonsterAttack(1) >= Bot.LifePoints + && Duel.Phase == DuelPhase.Main1 && (CurrentTiming & hintTimingMainEnd) != 0 && Duel.Turn > 1) + { + activatedCardIdList.Add(CardId.DogmatikaFleurdelis); + List enemyTargetList = ShuffleCardList(Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsDisabled()).ToList()); + if (enemyTargetList.Count() > 0) + { + AI.SelectYesNo(true); + AI.SelectCard(enemyTargetList); + } + else + { + AI.SelectYesNo(false); + } + return true; + } + } + if (Card.Location == CardLocation.MonsterZone) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool DogmatikaFleurdelisDelayActivate() + { + if (CheckWhetherNegated()) return false; + if (Card.Location == CardLocation.Hand) + { + bool checkFlag = false; + bool notQuickTiming = Duel.LastChainPlayer == -1 && CurrentTiming <= 0; + if (Duel.Player == 0 && Duel.Phase == DuelPhase.Main1 && notQuickTiming && Duel.Turn > 1) + { + Logger.DebugWriteLine("=== timing: " + CurrentTiming.ToString()); + int attack = Util.GetBestAttack(Bot); + List currentBetterList = Enemy.MonsterZone.GetMonsters().Where(card => card.GetDefensePower() >= attack).ToList(); + List newBetterList = Enemy.MonsterZone.GetMonsters().Where(card => card.GetDefensePower() >= 3000).ToList(); + if (currentBetterList.Count() > newBetterList.Count()) checkFlag = true; + } + if ((Bot.HasInHandOrInSpellZone(CardId.SinfulSpoilsOfDoom_Rciela) && Duel.Player == 0 && notQuickTiming) + || Bot.GetSpells().Any(card => card.IsCode(CardId.SinfulSpoilsOfDoom_Rciela) && card.IsFacedown())) + { + if (!Bot.GetMonsters().Any(card => card.IsFaceup() && card.Level >= 7 && card.HasRace(CardRace.SpellCaster))) checkFlag = true; + } + if (checkFlag) + { + List enemyTargetList = ShuffleCardList(Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsDisabled()).ToList()); + if (enemyTargetList.Count() > 0) + { + AI.SelectYesNo(true); + AI.SelectCard(enemyTargetList); + } + else AI.SelectYesNo(false); + activatedCardIdList.Add(CardId.DogmatikaFleurdelis); + return true; + } + } + return false; + } + + public bool DogmatikaMaximusActivate() + { + if (CheckWhetherNegated()) return false; + if (Card.Location == CardLocation.Hand) + { + if (CheckShouldNoMoreSpSummon()) return false; + // banish dump extra + List dumpIdCheck = new List{ CardId.ElderEntityNtss, CardId.GaruraWingsOfResonantLife, CardId.DespianLuluwalilith }; + foreach (int dumpId in dumpIdCheck) + { + IEnumerable checkList = Bot.GetGraveyardMonsters().Where(card => card.IsCode(dumpId)); + if (checkList.Count() > 1) + { + IEnumerable notSummonList = checkList.Where(card => card.ProcCompleted == 0); + if (notSummonList.Count() > 0) + { + AI.SelectCard(notSummonList.ToList()); + return true; + } + AI.SelectCard(checkList.ToList()); + return true; + } + } + + // find not summoned card + List notSummonedList = ShuffleCardList(Bot.Graveyard.Where(card => card != null && card.IsMonster() && card.ProcCompleted == 0 + && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)).ToList()); + if (notSummonedList.Count() > 0) + { + AI.SelectCard(notSummonedList); + return true; + } + + // sort by attack + List graveTargetList = Bot.Graveyard.Where(card => card != null && card.IsMonster() + && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)).OrderBy(card => card.Attack).ToList(); + if (graveTargetList.Count() > 0) + { + AI.SelectCard(graveTargetList); + return true; + } + } + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherWillbeRemoved()) return false; + List decidedToDiscard = new List(); + List checkDiscardIdList = new List{ CardId.ElderEntityNtss, CardId.HeraldOfTheArcLight, CardId.GaruraWingsOfResonantLife, + CardId.TitanikladTheAshDragon, CardId.GranguignolTheDuskDragon, CardId.PSYFramelordOmega, CardId.DespianLuluwalilith }; + foreach (int checkId in checkDiscardIdList) + { + if (Bot.HasInExtra(checkId) && !activatedCardIdList.Contains(checkId)) + { + if (checkId == CardId.ElderEntityNtss) + { + List destroyList = GetNormalEnemyTargetList(true, false); + if (destroyList.Count() == 0) continue; + } + if (enemyActivateLockBird && (checkId == CardId.HeraldOfTheArcLight || checkId == CardId.GaruraWingsOfResonantLife)) + { + continue; + } + if (checkId == CardId.HeraldOfTheArcLight) + { + if (Bot.HasInMonstersZone(CardId.DogmatikaAlbaZoa)) continue; + if (GetNeedSearchRitualCardIdList().Count() == 0) continue; + } + if (checkId == CardId.GaruraWingsOfResonantLife && activatedCardIdList.Contains(CardId.GaruraWingsOfResonantLife)) + { + continue; + } + if (discardExtraThisTurn.Contains(checkId) && (checkId == CardId.TitanikladTheAshDragon || checkId == CardId.DespianLuluwalilith)) + { + continue; + } + decidedToDiscard.Add(checkId); + } + } + + maximusDiscardExtraIdList.AddRange(decidedToDiscard); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + return true; + } + + return false; + } + + public bool DiabellstarTheBlackWitchSpSummon() + { + bool hasEmptyMonsterZone = false; + for (int i = 0; i < 5; ++ i) + { + if (Bot.MonsterZone[i] == null) + { + hasEmptyMonsterZone = true; + break; + } + } + if (hasEmptyMonsterZone) + { + if (Bot.HasInHandOrInSpellZone(CardId.WANTED_SeekerOfSinfulSpoils)) + { + AI.SelectCard(CardId.WANTED_SeekerOfSinfulSpoils); + return true; + } + if (activatedMatrixList.Count() > 0) + { + AI.SelectCard(activatedMatrixList); + return true; + } + if (Bot.GetSpells().Where(card => card.IsCode(CardId.DogmatikaMatrix)).Count() > 1) + { + AI.SelectCard(CardId.DogmatikaMatrix); + return true; + } + } + if (!Bot.HasInHand(CardId.DogmatikaFleurdelis) || Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika)).Count() > 1) + { + List checkIdList = new List{ CardId.DogmatikaEcclesia, CardId.DogmatikaMaximus}; + foreach (int checkId in checkIdList) + { + ClientCard costMonster = null; + if (activatedCardIdList.Contains(checkId)) costMonster = Bot.GetMonsters().FirstOrDefault(card => card.IsCode(checkId)); + if (costMonster == null) costMonster = Bot.GetMonsters().FirstOrDefault(card => card.IsCode(checkId) && card.IsDisabled()); + if (costMonster != null) + { + AI.SelectCard(costMonster); + return true; + } + } + } + if (hasEmptyMonsterZone) + { + List checkIdList = new List { CardId.KnightmareCorruptorIblee, CardId.ThesIrisSwordsoul }; + foreach (int checkId in checkIdList) + { + if (Bot.HasInHand(checkId)) + { + AI.SelectCard(checkId); + return true; + } + } + } + List faceDownMonsters = Bot.GetMonsters().Where(card => card.IsFacedown()).OrderBy(card => { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(card.Id); + if (cardData != null) + { + return cardData.Attack; + } + return card.Attack; + }).ToList(); + if (faceDownMonsters.Count() > 0) + { + AI.SelectCard(faceDownMonsters); + return true; + } + if (hasEmptyMonsterZone) + { + if (Bot.HasInHand(CardId.DogmatikaFleurdelis) && Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika)).Count() == 0) + { + AI.SelectCard(CardId.DogmatikaFleurdelis); + return true; + } + if (CheckRemainInDeck(CardId.DogmatikaMacabre) > 0) + { + ClientCard albaZoaInHand = Bot.Hand.FirstOrDefault(card => card.IsCode(CardId.DogmatikaAlbaZoa)); + if (albaZoaInHand != null) + { + AI.SelectCard(albaZoaInHand); + return true; + } + } + List dumpIdList = new List{ CardId.DogmatikaPunishment, _CardId.InfiniteImpermanence, _CardId.CalledByTheGrave, _CardId.AshBlossom, _CardId.MaxxC }; + foreach (int dumpId in dumpIdList) + { + int checkCount = Bot.Hand.Where(card => card.IsCode(dumpId)).Count(); + checkCount += Bot.SpellZone.Where(card => card != null && card.IsCode(dumpId)).Count(); + if (checkCount > 1) + { + AI.SelectCard(dumpId); + return true; + } + } + List extraCheckList = Bot.GetMonsters().Where(card => card.HasType(CardType.Fusion | CardType.Synchro | CardType.Link)) + .OrderBy(card => card.Attack).ToList(); + foreach (ClientCard checkCard in extraCheckList) + { + if (!Bot.HasInHand(CardId.DogmatikaFleurdelis) || CheckHasExtraOnField(checkCard)) + { + AI.SelectCard(checkCard); + return true; + } + } + } + if (Bot.GetMonsterCount() == 0 || CheckRemainInDeck(CardId.SinfulSpoilsOfDoom_Rciela) > 0) + { + List spellIdList = new List{ _CardId.CrossoutDesignator, _CardId.InfiniteImpermanence, _CardId.CalledByTheGrave, + CardId.DogmatikaPunishment, CardId.DogmatikaMacabre, CardId.DogmatikaLamity }; + foreach (int spellId in spellIdList) + { + if (Bot.HasInHandOrInSpellZone(spellId)) + { + AI.SelectCard(spellId); + return true; + } + } + } + + return false; + } + + public bool DiabellstarTheBlackWitchActivate() + { + if (CheckWhetherNegated()) return false; + if (Card.Location == CardLocation.MonsterZone) + { + AI.SelectCard(CardId.SinfulSpoilsOfDoom_Rciela, CardId.WANTED_SeekerOfSinfulSpoils); + SelectSTPlace(); + activatedCardIdList.Add(Card.Id); + return true; + } + + return false; + } + + public bool DogmatikaEcclesiaSummon() + { + if (enemyActivateLockBird) return false; + if (CheckWhetherNegated()) return false; + if (activatedCardIdList.Contains(Card.Id)) return false; + + summoned = true; + return true; + } + + public bool DogmatikaEcclesiaActivate() + { + if (CheckWhetherNegated()) return false; + // sp summon + if (Card.Location == CardLocation.Hand) + { + if (activatedCardIdList.Contains(Card.Id)) return false; + if (CheckShouldNoMoreSpSummon()) + { + if (!Bot.HasInHand(CardId.DogmatikaFleurdelis) || Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika))) + { + return false; + } + } + if (enemyActivateLockBird) + { + if (Bot.HasInHand(CardId.DogmatikaFleurdelis) && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika))) + { + return true; + } + return false; + } + return true; + } + // search + if (Card.Location == CardLocation.MonsterZone) + { + if ((Duel.Player == 0 && Duel.Phase == DuelPhase.End) || (Duel.Player == 1 && Duel.Phase < DuelPhase.End)) + { + if (!Bot.HasInHand(CardId.DogmatikaFleurdelis) && CheckRemainInDeck(CardId.DogmatikaFleurdelis) > 0) + { + AI.SelectCard(CardId.DogmatikaFleurdelis); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + return true; + } + } + + // for maxxc + if (CheckAtAdvantage() && enemyActivateMaxxC) + { + List checkIdListFirstPart = new List{ CardId.DogmatikaPunishment, CardId.DogmatikaFleurdelis }; + if (DogmatikaMatrixCanActivate()) + { + checkIdListFirstPart.Add(CardId.DogmatikaMatrix); + } + checkIdListFirstPart.AddRange(new List{ CardId.DogmatikaMaximus, CardId.DogmatikaAlbaZoa, CardId.DogmatikaMacabre, CardId.DogmatikaLamity }); + checkIdListFirstPart.Add(CardId.DogmatikaMatrix); + foreach (int checkId in checkIdListFirstPart) + { + if (!Bot.HasInHandOrInSpellZone(checkId) && CheckRemainInDeck(checkId) > 0) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + return true; + } + } + } + + // search matrix + bool canSearchMatrix = DogmatikaMatrixCanActivate() && !activatedCardIdList.Contains(CardId.DogmatikaMatrix) + && CheckRemainInDeck(CardId.DogmatikaMatrix) > 0 && !Bot.HasInHand(CardId.DogmatikaMatrix); + if (canSearchMatrix && Enemy.GetMonsterCount() > 0) + { + AI.SelectCard(CardId.DogmatikaMatrix); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + return true; + } + + // search for ritual + List needSearchRitualIdList = GetNeedSearchRitualCardIdList(); + bool canSearchMaximus = CheckRemainInDeck(CardId.DogmatikaMaximus) > 0 && !activatedCardIdList.Contains(CardId.DogmatikaMaximus) + && Bot.Graveyard.Where(card => card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)).Count() > 0; + if (needSearchRitualIdList.Count() > 0) + { + // matrix + if (canSearchMatrix) + { + AI.SelectCard(CardId.DogmatikaMatrix); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + return true; + } + // maximus + if (canSearchMaximus && Bot.HasInExtra(CardId.HeraldOfTheArcLight)) + { + AI.SelectCard(CardId.DogmatikaMaximus); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + return true; + } + + } + + // search maximus + if (canSearchMaximus) + { + AI.SelectCard(CardId.DogmatikaMaximus); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + return true; + } + + List checkIdListSecondPart = new List{ CardId.DogmatikaPunishment, CardId.DogmatikaFleurdelis }; + if (DogmatikaMatrixCanActivate()) + { + checkIdListSecondPart.Add(CardId.DogmatikaMatrix); + } + checkIdListSecondPart.AddRange(new List{ CardId.DogmatikaMaximus, CardId.DogmatikaAlbaZoa, CardId.DogmatikaMacabre, CardId.DogmatikaLamity }); + checkIdListSecondPart.Add(CardId.DogmatikaMatrix); + foreach (int checkId in checkIdListSecondPart) + { + if (!Bot.HasInHandOrInSpellZone(checkId) && CheckRemainInDeck(checkId) > 0) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + return true; + } + } + } + + return false; + } + + public bool AshBlossomActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (CheckAtAdvantage() && Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + return false; + } + if (DefaultAshBlossomAndJoyousSpring()) + { + CheckDeactiveFlag(); + return true; + } + return false; + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated() || Duel.LastChainPlayer == 0) return false; + if (Enemy.HasInMonstersZone(CardId.KnightmareCorruptorIblee, true, false, true) && !confirmLink2) return false; + return DefaultMaxxC(); + } + + public bool KnightmareCorruptorIbleeSummon() + { + if (banSpSummonFromExTurn > 0) return false; + if (CheckWhetherWillbeRemoved()) return false; + if (activatedCardIdList.Contains(CardId.KnightmareCorruptorIblee)) return false; + if (Bot.HasInExtra(CardId.SalamangreatAlmiraj) || Bot.HasInExtra(CardId.Linguriboh)) + { + summoned = true; + return true; + } + + if (Bot.HasInExtra(CardId.SPLittleKnight)) + { + // TODO + } + + return false; + } + + public bool KnightmareCorruptorIbleeActivate() + { + if (Util.IsTurn1OrMain2()) return true; + if (Duel.Turn > 1) + { + if (Bot.HasInHand(CardId.DogmatikaMatrix) && DogmatikaMatrixCanActivate()) + { + return true; + } + if (Enemy.GetMonsterCount() > 0) + { + return true; + } + } + return false; + } + + public bool NadirServantActivate() + { + if (CheckWhetherNegated() || CheckWhetherWillbeRemoved()) return false; + ClientCard discardExtra = null; + int searchId = 0; + + // search ecclesia + if (!activatedCardIdList.Contains(CardId.DogmatikaEcclesia) && CheckCalledbytheGrave(CardId.DogmatikaEcclesia) == 0 && !Bot.HasInHand(CardId.DogmatikaEcclesia)) + { + if (CheckHasExtraOnField() || !summoned) + { + if (Bot.HasInGraveyard(CardId.DogmatikaEcclesia) || CheckRemainInDeck(CardId.DogmatikaEcclesia) > 0) + { + searchId = CardId.DogmatikaEcclesia; + discardExtra = GetExtraToDiscard(1500, null); + } + } + } + + // search maximus + if (searchId == 0 || discardExtra == null) + { + if (!activatedCardIdList.Contains(CardId.DogmatikaMaximus) && CheckCalledbytheGrave(CardId.DogmatikaMaximus) == 0 + && Bot.HasInGraveyard(CardId.DogmatikaMaximus) || CheckRemainInDeck(CardId.DogmatikaMaximus) > 0) + { + searchId = CardId.DogmatikaMaximus; + discardExtra = GetExtraToDiscard(1500, null); + } + } + + // search Fleurdelis + if (searchId == 0 || discardExtra == null) + { + if (!Bot.HasInHand(CardId.DogmatikaFleurdelis) && Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika)) + && Bot.HasInGraveyard(CardId.DogmatikaFleurdelis) || CheckRemainInDeck(CardId.DogmatikaFleurdelis) > 0) + { + searchId = CardId.DogmatikaFleurdelis; + discardExtra = GetExtraToDiscard(2500, null); + } + } + + // search ecclesia for next turn + if (searchId == 0 || discardExtra == null) + { + if (Bot.HasInGraveyard(CardId.DogmatikaEcclesia) || CheckRemainInDeck(CardId.DogmatikaEcclesia) > 0) + { + searchId = CardId.DogmatikaEcclesia; + discardExtra = GetExtraToDiscard(1500, null); + } + } + + if (discardExtra != null && searchId > 0) + { + discardExtraThisTurn.Add(discardExtra?.Id ?? 0); + AI.SelectCard(discardExtra); + ClientCard targetInGY = Bot.Graveyard.FirstOrDefault(card => card != null && card.IsCode(searchId)); + if (targetInGY != null) + { + AI.SelectNextCard(targetInGY); + } else { + AI.SelectNextCard(searchId); + } + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + SelectSTPlace(null, true); + return true; + } + + return false; + } + + public bool DogmatikaLamityActivate() + { + if (CheckWhetherNegated()) return false; + // use lilith + if (Bot.HasInExtra(CardId.DespianLuluwalilith)) + { + AI.SelectYesNo(true); + AI.SelectCard(CardId.DogmatikaAlbaZoa); + AI.SelectNextCard(CardId.DespianLuluwalilith); + discardExtraThisTurn.Add(CardId.DespianLuluwalilith); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + SelectSTPlace(null, true); + return true; + } + + return false; + } + + public bool DogmatikaLamityDelayActivate() + { + if (CheckWhetherNegated() || Bot.HasInExtra(CardId.DespianLuluwalilith)) return false; + if (Bot.HasInMonstersZone(CardId.DogmatikaAlbaZoa, false, false, true)) return false; + List materialList = new List(); + int totalLevel = 0; + List faceDownMonsterList = Bot.GetMonsters().Where(card => !card.HasType(CardType.Xyz) && card.IsFacedown()).OrderByDescending(card => card.Level).ToList(); + foreach (ClientCard faceDownMonster in faceDownMonsterList) + { + materialList.Add(faceDownMonster); + totalLevel += faceDownMonster.Level; + if (totalLevel >= 12) break; + } + + ClientCard handSummonTarget = Bot.Hand.FirstOrDefault(card => card.IsCode(CardId.DogmatikaAlbaZoa)); + if (handSummonTarget == null) return false; + int extraUseCount = 0; + List extraMonsterList = Bot.GetMonsters().Where(card => !card.HasType(CardType.Xyz | CardType.Link) && card.IsFaceup()) + .OrderByDescending(card => card.Level).ToList(); + extraMonsterList.AddRange(Bot.Hand.Where(card => card.IsMonster() && card != handSummonTarget).OrderByDescending(card => card.Level).ToList()); + foreach (ClientCard faceUpMonster in extraMonsterList) + { + if (totalLevel >= 12 || extraUseCount >= 1) break; + materialList.Add(faceUpMonster); + totalLevel += faceUpMonster.Level; + extraUseCount ++; + } + + if (totalLevel >= 12) + { + AI.SelectYesNo(false); + AI.SelectCard(CardId.DogmatikaAlbaZoa); + AI.SelectNextCard(materialList); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(1); + SelectSTPlace(null, true); + return true; + } + + return false; + } + + public bool DogmatikaMacabreActivate() + { + if (Bot.HasInMonstersZone(CardId.DogmatikaAlbaZoa)) return false; + if (CheckWhetherNegated()) return false; + List gyMaterialList = Bot.Graveyard.Where(card => card != null && card.HasType(CardType.Fusion | CardType.Synchro)) + .OrderByDescending(card => card.Level).ToList(); + List selectMaterialList = new List(); + int totalLevel = 0; + List checkDiscardThisTurnIdList = new List { CardId.DespianLuluwalilith, CardId.TitanikladTheAshDragon }; + foreach (ClientCard material in gyMaterialList) + { + if (material.IsCode(CardId.PSYFramelordOmega)) continue; + if (CheckAtAdvantage()) + { + foreach (int checkId in checkDiscardThisTurnIdList) + { + if (material.IsCode(checkId) && discardExtraThisTurn.Contains(checkId)) continue; + } + } + totalLevel += material.Level; + selectMaterialList.Add(material); + if (totalLevel >= 12) break; + } + if (totalLevel >= 12) + { + ClientCard graveAlbaZoa = Bot.Graveyard.FirstOrDefault(card => card.IsCode(CardId.DogmatikaAlbaZoa)); + if (graveAlbaZoa != null) + { + AI.SelectCard(graveAlbaZoa); + } else { + AI.SelectCard(CardId.DogmatikaAlbaZoa); + } + AI.SelectMaterials(selectMaterialList, HintMsg.Release); + SelectSTPlace(null, true); + return true; + } + + return false; + } + + public bool SinfulSpoilsOfDoom_RcielaActivate() + { + // select self target + ClientCard selfTarget = null; + bool activateFlag = false; + List selfCasterList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.Level >= 7 && card.HasRace(CardRace.SpellCaster)) + .OrderByDescending(card => card.Attack).ThenByDescending(card => card.Level).ToList(); + bool onlyAlbaZoa = selfCasterList.Count() == 1 && selfCasterList[0].IsCode(CardId.DogmatikaAlbaZoa); + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && lastChainCard.Controller == 1 && lastChainCard.IsMonster()) + { + bool negateFlag = lastChainCard.IsCode(_CardId.EffectVeiler, CardId.GhostMournerMoonlitChill); + if (Duel.Turn > 1 || !negateFlag) + { + foreach (ClientCard chainTarget in Duel.LastChainTargets) + { + if (selfCasterList.Contains(chainTarget) && (!negateFlag || !chainTarget.IsCode(CardId.DiabellstarTheBlackWitch))) + { + selfTarget = chainTarget; + activateFlag = true; + break; + } + } + } + } + if (selfTarget == null && !onlyAlbaZoa) + { + selfTarget = selfCasterList.FirstOrDefault(card => !card.IsCode(CardId.DogmatikaAlbaZoa)); + } + if (DefaultOnBecomeTarget() && !onlyAlbaZoa) + { + activateFlag = true; + } + + if (selfTarget != null) + { + int targetAttack = selfTarget.Attack; + // destroy danger monster + ClientCard dangerCard = GetProblematicEnemyMonster(-1, true, true); + if (dangerCard != null) + { + activateFlag = true; + } + + // destroy multi monster + if (!onlyAlbaZoa) + { + List toDestroyMonsterList = Enemy.GetMonsters().Where(card => card.IsFaceup() + && card.Attack > 0 && card.Attack <= targetAttack && !currentDestroyCardList.Contains(card) + && (Duel.Player == 1 || card != Enemy.BattlingMonster)).ToList(); + if (toDestroyMonsterList.Count() > 1) + { + activateFlag = true; + currentDestroyCardList.AddRange(toDestroyMonsterList); + } + } + + // decrease attack + int botWorstPower = Util.GetWorstBotMonster()?.GetDefensePower() ?? 0; + bool decreaseFlag = Duel.Player == 1 && Enemy.GetMonsters().Any(card => card.Attack >= botWorstPower + && card.IsMonsterHasPreventActivationEffectInBattle()) && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2; + decreaseFlag |= (!onlyAlbaZoa || (Bot.BattlingMonster?.IsCode(CardId.DogmatikaAlbaZoa) ?? false)) + && (Bot.BattlingMonster?.GetDefensePower() ?? 0) <= (Enemy.BattlingMonster?.GetDefensePower() ?? 0) + && Duel.LastChainPlayer != 0 && (CurrentTiming & hintDamageStep) != 0 && CurrentTiming > 0; + if (decreaseFlag) + { + activateFlag = true; + } + } + + if (activateFlag) + { + SelectSTPlace(null, true); + AI.SelectCard(selfTarget); + return true; + } + + return false; + } + + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (CheckAtAdvantage() && Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + return false; + } + if (Duel.LastChainPlayer == 1) + { + // negate + if (Util.GetLastChainCard().IsMonster()) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + if (CheckCalledbytheGrave(code) > 0) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC) && CheckAtAdvantage()) + { + return false; + } + if (Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.IsOriginalCode(code)) != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + currentNegatingIdList.Add(code); + CheckDeactiveFlag(); + return true; + } + } + + // banish target + foreach (ClientCard cards in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(cards) && cards.IsMonster()) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + int code = cards.GetOriginCode(); + AI.SelectCard(cards); + currentNegatingIdList.Add(code); + return true; + } + } + + // become targets + if (Duel.ChainTargets.Contains(Card)) + { + List enemyMonsters = Enemy.Graveyard.GetMatchingCards(card => card.IsMonster()).ToList(); + if (enemyMonsters.Count() > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + int code = enemyMonsters[0].Id; + AI.SelectCard(enemyMonsters); + currentNegatingIdList.Add(code); + return true; + } + } + } + + // avoid danger monster in grave + if (Duel.LastChainPlayer == 1) return false; + List targets = GetDangerousCardinEnemyGrave(true); + if (targets.Count() > 0) { + int code = targets[0].Id; + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(targets); + currentNegatingIdList.Add(code); + return true; + } + + return false; + } + + public bool CrossoutDesignatorActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + // negate + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard() != null) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + // do not negate black witch + if (code == CardId.DiabellstarTheBlackWitch) return false; + if (CheckCalledbytheGrave(code) > 0) return false; + if (CheckRemainInDeck(code) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectAnnounceID(code); + currentNegatingIdList.Add(code); + CheckDeactiveFlag(); + return true; + } + } + return false; + } + + public bool WANTED_SeekerOfSinfulSpoilsActivate() + { + if (Card.Location == CardLocation.Hand || (Card.Location == CardLocation.SpellZone && Card.HasPosition(CardPosition.FaceDown))) + { + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); + return true; + } + + return true; + } + + public bool DogmatikaMatrixCanActivate() + { + return CheckRemainInDeck(CardId.DogmatikaAlbaZoa, CardId.DogmatikaLamity, CardId.DogmatikaMacabre) > 0; + } + + public bool DogmatikaMatrixActivate() + { + if (CheckWhetherNegated()) return false; + + // activate for search + if (Card.Location == CardLocation.Hand || (Card.Location == CardLocation.SpellZone && Card.HasPosition(CardPosition.FaceDown))) + { + List neededRitualCardIdList = GetNeedSearchRitualCardIdList(); + // can only search ritual + if (Enemy.GetMonsterCount() == 0) + { + if (!Bot.MonsterZone.Any(card => card != null && card.IsFaceup() && card.HasType(CardType.Ritual) && card.HasSetcode(SetcodeDogmatika)) + && neededRitualCardIdList.Count() <= 0) + { + return false; + } + SelectSTPlace(null, true); + AI.SelectCard(neededRitualCardIdList); + // in case enemy summon monster after activated + AI.SelectYesNo(true); + activatedCardIdList.Add(Card.Id); + return true; + } + // can search both + else + { + if (neededRitualCardIdList.Count() <= 0) + { + SelectSTPlace(null, true); + AI.SelectYesNo(true); + // search both monster and spell + if (CheckRemainInDeck(CardId.DogmatikaAlbaZoa) > 0 && CheckRemainInDeck(CardId.DogmatikaLamity, CardId.DogmatikaMacabre) > 0) + { + AI.SelectCard(CardId.DogmatikaAlbaZoa); + AI.SelectNextCard(CardId.DogmatikaLamity, CardId.DogmatikaMacabre); + } + else + { + AI.SelectCard(CardId.DogmatikaAlbaZoa, CardId.DogmatikaLamity, CardId.DogmatikaMacabre); + DogmatikaMatrixNextSearch(); + } + activatedCardIdList.Add(Card.Id); + return true; + } + else + { + SelectSTPlace(null, true); + AI.SelectCard(neededRitualCardIdList); + AI.SelectYesNo(true); + DogmatikaMatrixNextSearch(); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + // discard extra + else + { + int option = 0; + if (CheckWhetherWillbeRemoved()) option = 1; + if (!checkedEnemyExtra && Enemy.ExtraDeck.Count() > 0) option = 1; + if (Enemy.HasInMonstersZone(CardId.KnightmareCorruptorIblee) && avoid2Monster) option = 1; + if (!Bot.HasInExtra(CardId.ElderEntityNtss) || GetNormalEnemyTargetList(true, false, false).Count() <= 0) + { + List checkActivatedIdList = new List{ CardId.GaruraWingsOfResonantLife, CardId.DespianLuluwalilith, + CardId.TitanikladTheAshDragon, CardId.GranguignolTheDuskDragon, CardId.PSYFramelordOmega }; + bool checkFlag = false; + foreach (int checkId in checkActivatedIdList) + { + checkFlag |= !discardExtraThisTurn.Contains(checkId) && !activatedCardIdList.Contains(checkId) && Bot.HasInExtra(checkId); + } + if (!checkFlag) + { + option = 1; + } + } + + Logger.DebugWriteLine("===Matrix option: " + option.ToString()); + matrixActivating = true; + AI.SelectOption(option); + activatedMatrixList.Add(Card); + return true; + } + } + + public void DogmatikaMatrixNextSearch() + { + List fieldMonsterList = Bot.GetMonsters(); + fieldMonsterList.AddRange(Enemy.GetMonsters()); + bool hasExtraOnField = fieldMonsterList.Any(card => card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)); + + // search ecclesia + if (!activatedCardIdList.Contains(CardId.DogmatikaEcclesia) && CheckCalledbytheGrave(CardId.DogmatikaEcclesia) == 0) + { + if (hasExtraOnField || !summoned) + { + if (CheckRemainInDeck(CardId.DogmatikaEcclesia) > 0) + { + AI.SelectNextCard(CardId.DogmatikaEcclesia); + return; + } + } + } + + // seach Maximus + if (CheckRemainInDeck(CardId.DogmatikaMaximus) > 0 && !activatedCardIdList.Contains(CardId.DogmatikaMaximus) + && Bot.Graveyard.Where(card => card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)).Count() > 0) + { + AI.SelectNextCard(CardId.DogmatikaMaximus); + return; + } + + // search Fleurdelis + if (CheckRemainInDeck(CardId.DogmatikaFleurdelis) > 0 && !Bot.HasInHand(CardId.DogmatikaFleurdelis) && hasExtraOnField + && Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika))) + { + AI.SelectNextCard(CardId.DogmatikaFleurdelis); + return; + } + + List searchIdList = new List{ CardId.DogmatikaPunishment, CardId.DogmatikaEcclesia, CardId.DogmatikaMatrix, + CardId.DogmatikaMaximus, CardId.DogmatikaFleurdelis, CardId.DogmatikaAlbaZoa, CardId.DogmatikaLamity, CardId.DogmatikaMacabre }; + foreach (int searchId in searchIdList) + { + if (CheckRemainInDeck(searchId) > 0) + { + AI.SelectNextCard(searchId); + return; + } + } + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + + ClientCard LastChainCard = Util.GetLastChainCard(); + + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ( (this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || Util.IsChainTarget(Card) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + CheckDeactiveFlag(); + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + if (target != null) + { + AI.SelectCard(target); + } else { + AI.SelectCard(Enemy.GetMonsters()); + } + infiniteImpermanenceList.Add(this_seq); + return true; + } + } + + // negate monster + List shouldNegateList = GetMonsterListForTargetNegate(false, true); + if (shouldNegateList.Count() > 0) + { + ClientCard negateTarget = shouldNegateList[0]; + currentNegateMonsterList.Add(negateTarget); + + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++ i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(negateTarget); + return true; + } + + return false; + } + + public bool DogmatikaPunishmentActivate() + { + if (CheckWhetherNegated() || CheckWhetherWillbeRemoved()) return false; + + ClientCard targetCard = null; + ClientCard extraToDiscard = null; + + // destroy problem card by ntss + List targetList = GetProblematicEnemyCardList(true, true); + if (targetList.Count() > 0 && Duel.LastChainPlayer != 0 && Bot.HasInExtra(CardId.ElderEntityNtss)) + { + foreach (ClientCard target in targetList) + { + if (target.IsFaceup() && target.IsMonster() && target.Attack <= 2500) + { + targetCard = target; + extraToDiscard = GetExtraToDiscard(2500, target); + if (extraToDiscard != null) + { + break; + } + } + } + if (targetCard == null || extraToDiscard == null) + { + List enemyMonsterList = Enemy.GetMonsters().Where(card => card.IsFaceup() + && !card.IsShouldNotBeTarget() && card.IsShouldNotBeSpellTrapTarget()).ToList(); + enemyMonsterList.Sort(CardContainer.CompareCardAttack); + foreach (ClientCard target in enemyMonsterList) + { + if (target.IsFaceup() && target.IsMonster() && target.Attack <= 2500) + { + targetCard = target; + extraToDiscard = GetExtraToDiscard(2500, target); + if (extraToDiscard != null) + { + break; + } + } + } + } + } + + if (targetCard == null || extraToDiscard == null) + { + targetCard = GetProblematicEnemyMonster(0, true, true); + if (targetCard != null) + { + extraToDiscard = GetExtraToDiscard(targetCard.Attack, targetCard); + } + } + + if (targetCard == null || extraToDiscard == null) + { + bool check1 = DefaultOnBecomeTarget(); + bool check2 = Bot.UnderAttack && (Bot.BattlingMonster?.GetDefensePower() ?? 0) <= (Enemy.BattlingMonster?.GetDefensePower() ?? 0) && Duel.LastChainPlayer != 0;; + bool check3 = Duel.Player == 1 && Duel.Phase == DuelPhase.End && Duel.LastChainPlayer != 0; + bool check4 = Duel.Player == 1 && avoid2Monster && Enemy.GetMonsterCount() >= 2 && Duel.LastChainPlayer != 0; + Logger.DebugWriteLine("===punishment check flag: " + check1 + " " + check2 + " " + check3 + " " + check4); + if (check1 || check2 || check3 || check4) + { + List checkList = Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsShouldNotBeTarget() && !currentDestroyCardList.Contains(card)) + .OrderByDescending(c => c.Attack).ToList(); + foreach (ClientCard checkTarget in checkList) + { + extraToDiscard = GetExtraToDiscard(checkTarget.Attack, checkTarget); + if (extraToDiscard != null) + { + targetCard = checkTarget; + break; + } + } + } + } + + if (targetCard != null && extraToDiscard != null) + { + AI.SelectCard(targetCard); + AI.SelectNextCard(extraToDiscard); + currentDestroyCardList.Add(targetCard); + discardExtraThisTurn.Add(extraToDiscard?.Id ?? 0); + activatedCardIdList.Add(Card.Id); + UpdateBanSpSummonFromExTurn(2); + return true; + } + + return false; + } + + + public bool GranguignolTheDuskDragonActivate() + { + if (Card.Location == CardLocation.Grave) + { + AI.SelectCard(CardId.DespianLuluwalilith, CardId.DogmatikaEcclesia, CardId.DogmatikaMaximus, CardId.DogmatikaFleurdelis); + return true; + } + return false; + } + + public bool TitanikladTheAshDragonActivate() + { + if (!activatedCardIdList.Contains(CardId.DogmatikaEcclesia) && CheckRemainInDeck(CardId.DogmatikaEcclesia) > 0 && CheckCalledbytheGrave(CardId.DogmatikaEcclesia) == 0) + { + AI.SelectOption(1); + AI.SelectCard(CardId.DogmatikaEcclesia); + return true; + } + if (CheckRemainInDeck(CardId.DogmatikaFleurdelis) > 0) + { + if (!Bot.HasInHand(CardId.DogmatikaFleurdelis) && !enemyActivateLockBird) + { + if (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika))) + { + AI.SelectOption(0); + AI.SelectCard(CardId.DogmatikaFleurdelis); + return true; + } + } + if (Duel.Player == 1 && Enemy.GetMonsterCount() == 0) + { + AI.SelectOption(1); + AI.SelectCard(CardId.DogmatikaFleurdelis); + return true; + } + } + if (CheckRemainInDeck(CardId.DogmatikaMaximus) > 0) + { + AI.SelectOption(1); + AI.SelectCard(CardId.DogmatikaMaximus); + return true; + } + + return false; + } + + public bool GaruraWingsOfResonantLifeActivate() + { + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool ElderEntityNtssActivate() + { + if (Card.Location == CardLocation.Grave) + { + List destroyList = GetNormalEnemyTargetList(true, true, true); + if (destroyList.Count() > 0) + { + currentDestroyCardList.Add(destroyList[0]); + AI.SelectCard(destroyList); + return true; + } + } + + return false; + } + + public bool DespianLuluwalilithActivate() + { + // spsummon + if (Card.Location == CardLocation.Grave) + { + if (!activatedCardIdList.Contains(CardId.DogmatikaEcclesia) && CheckRemainInDeck(CardId.DogmatikaEcclesia) > 0 + && CheckCalledbytheGrave(CardId.DogmatikaEcclesia) == 0 && !enemyActivateLockBird) + { + AI.SelectCard(CardId.DogmatikaEcclesia); + return true; + } + if (CheckRemainInDeck(CardId.ThesIrisSwordsoul) > 0) + { + AI.SelectCard(CardId.ThesIrisSwordsoul); + return true; + } + if (Duel.Turn > 1 && Enemy.GetMonsterCount() == 0 && CheckRemainInDeck(CardId.DogmatikaFleurdelis) > 0) + { + AI.SelectCard(CardId.DogmatikaFleurdelis); + return true; + } + if (Bot.HasInHand(CardId.DogmatikaFleurdelis) && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeDogmatika))) + { + List checkIdList = new List{ CardId.DogmatikaMaximus, CardId.DogmatikaEcclesia, CardId.DogmatikaFleurdelis }; + foreach (int checkId in checkIdList) + { + if (CheckRemainInDeck(checkId) > 0) + { + AI.SelectCard(checkId); + return true; + } + } + } + } + // increase atk & negate + if (Card.Location == CardLocation.MonsterZone) + { + List currentChainEnemyCard = Duel.CurrentChain.Where(card => card.Controller == 1 && !currentNegateMonsterList.Contains(card) + && (card.Location == CardLocation.MonsterZone || card.Location == CardLocation.SpellZone)).ToList(); + currentChainEnemyCard.AddRange(GetProblematicEnemyCardList(false, false)); + currentChainEnemyCard.AddRange(ShuffleCardList(Enemy.GetSpells().Where(card => card.IsFaceup()).ToList())); + currentChainEnemyCard.AddRange(ShuffleCardList(Enemy.GetMonsters().Where(card => card.IsFaceup()).ToList())); + if (currentChainEnemyCard.Count() > 0) + { + currentNegateMonsterList.Add(currentChainEnemyCard[0]); + AI.SelectYesNo(true); + AI.SelectCard(currentChainEnemyCard); + } + else AI.SelectYesNo(false); + return true; + } + return false; + } + + public bool PSYFramelordOmegaActivate() + { + if (Card.Location == CardLocation.Grave && omegaActivateCount <= 5) + { + if (CheckWhetherNegated()) return false; + List targets = GetDangerousCardinEnemyGrave(true); + if (targets.Count() > 0) { + AI.SelectCard(targets); + omegaActivateCount ++; + return true; + } + + List recycleExtraIdList = new List{ CardId.GaruraWingsOfResonantLife, CardId.ElderEntityNtss }; + foreach (int checkId in recycleExtraIdList) + { + if (!Bot.HasInExtra(checkId) && Bot.HasInGraveyard(checkId)) + { + AI.SelectCard(checkId); + omegaActivateCount ++; + return true; + } + } + + List recycleMainIdList = new List{ CardId.DogmatikaMacabre, CardId.DogmatikaLamity, CardId.DogmatikaAlbaZoa, CardId.DogmatikaPunishment, + CardId.DogmatikaEcclesia, CardId.DogmatikaFleurdelis, CardId.DogmatikaMatrix }; + foreach (int checkId in recycleMainIdList) + { + if (CheckRemainInDeck(checkId) <= 0 && Bot.HasInGraveyard(checkId)) + { + AI.SelectCard(checkId); + omegaActivateCount ++; + return true; + } + } + + recycleExtraIdList.AddRange(new List { + CardId.SuperStarslayerTYPHON, CardId.TitanikladTheAshDragon, CardId.HeraldOfTheArcLight, CardId.DespianLuluwalilith, CardId.Linguriboh, + CardId.SalamangreatAlmiraj, CardId.SecureGardna + }); + foreach (int checkId in recycleExtraIdList) + { + if (!Bot.HasInExtra(checkId) && Bot.HasInGraveyard(checkId)) + { + AI.SelectCard(checkId); + omegaActivateCount ++; + return true; + } + } + } + + return false; + } + + public bool HeraldOfTheArcLightActivate() + { + AI.SelectCard(GetNeedSearchRitualCardIdList()); + return true; + } + + public bool SuperStarslayerTYPHONSpSummon() + { + ClientCard material = Bot.GetMonsters().Where(card => card.IsFaceup()).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (material == null || material.Attack >= 3000) return false; + + bool checkFlag = GetProblematicEnemyMonster(material.Attack) != null; + checkFlag |= material.HasType(CardType.Link); + checkFlag |= material.Level <= 4; + if (checkFlag) + { + AI.SelectMaterials(material); + return true; + } + + return false; + } + + public bool SuperStarslayerTYPHONActivate() + { + if (CheckWhetherNegated()) return false; + List targetList = new List(); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsFloodgate() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsMonsterDangerous() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsMonsterInvincible() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.GetDefensePower() >= Util.GetBestAttack(Bot) && c.IsAttack()).OrderByDescending(card => card.Attack)); + if (Duel.Phase >= DuelPhase.Main2) + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link | CardType.SpSummon)).OrderByDescending(card => card.Attack)); + + if (targetList.Count() > 0) + { + targetList.AddRange(Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card)).OrderByDescending(card => card.Attack)); + targetList.AddRange(ShuffleCardList(Enemy.GetMonsters().Where(card => card.IsFacedown() && !targetList.Contains(card)).ToList())); + targetList.AddRange(ShuffleCardList(Bot.GetMonsters().Where(card => card.IsFacedown() && !targetList.Contains(card)).ToList())); + targetList.AddRange(Bot.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card)).OrderBy(card => card.Attack)); + AI.SelectCard(Card.Overlays); + AI.SelectNextCard(targetList); + return true; + } + + return false; + } + + public bool SPLittleKnightActivate() + { + // TODO + return false; + } + + public bool SecureGardnaSpSummon() + { + if (Bot.HasInHand(CardId.DogmatikaMaximus)) + { + if (!Bot.Graveyard.Any(card => card.IsMonster() && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link))) + { + return true; + } + } + return false; + } + + public bool LinguribohSpSummon() + { + if (Enemy.GetSpells().Any(card => card.IsFacedown())) return true; + if (!Bot.HasInExtra(CardId.SalamangreatAlmiraj)) return true; + return false; + } + + public bool LinguribohActivate() + { + if (CheckLastChainShouldNegated()) return true; + return false; + } + + public bool SalamangreatAlmirajSpSummon() + { + if (Bot.HasInMonstersZone(CardId.KnightmareCorruptorIblee, faceUp: true)) + { + AI.SelectMaterials(CardId.KnightmareCorruptorIblee); + return true; + } + if (Bot.HasInHand(new List{ CardId.DogmatikaEcclesia, CardId.DogmatikaMaximus, CardId.NadirServant })) + { + List materialList = Bot.MonsterZone.Where(card => card != null && card.IsFaceup() && card.Attack <= 1000 + && !card.HasType(CardType.Ritual | CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)).ToList(); + if (materialList.Count() > 0) + { + materialList.Sort(CardContainer.CompareCardAttack); + AI.SelectMaterials(materialList); + return true; + } + } + + return false; + } + + public bool SalamangreatAlmirajActivate() + { + if (Card.Location == CardLocation.Grave) return true; + bool checkFlag = Duel.Player == 1 && (!Bot.HasInHand(CardId.DogmatikaFleurdelis) || activatedCardIdList.Contains(CardId.DogmatikaFleurdelis)); + checkFlag |= DefaultOnBecomeTarget(); + checkFlag |= Bot.UnderAttack && Bot.BattlingMonster == Card; + if (checkFlag) + { + AI.SelectCard(Util.GetBestBotMonster()); + return true; + } + if (!Util.ChainContainsCard(CardId.SinfulSpoilsOfDoom_Rciela)) + { + List checkList = Bot.GetMonsters().Where(card => card.IsFaceup() && card != Card).OrderByDescending(card => card.Attack).ToList(); + checkList.AddRange(Bot.GetMonsters().GetMatchingCards(card => card.IsFacedown())); + foreach (ClientCard card in checkList) + { + if (Util.IsChainTarget(card)) + { + AI.SelectCard(card); + return true; + } + } + } + + return false; + } + + + public bool SummonForTYPHONCheck() + { + if (!Bot.HasInExtra(CardId.SuperStarslayerTYPHON) || Bot.GetMonsters().Any(card => card.IsFaceup()) || banSpSummonFromExTurn > 0) return false; + if (enemySpSummonFromExLastTurn < 2 && enemySpSummonFromExThisTurn < 2) return false; + if (Card.IsCode(CardId.KnightmareCorruptorIblee) && !CheckWhetherNegated()) return false; + if (Card.Level > 4) return false; + + int currentAttack = 0; + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(Card.Id); + if (cardData != null) currentAttack = cardData.Attack; + List summonList = Bot.Hand.Where(card => card.IsMonster() && card.Level <= 4).ToList(); + foreach (ClientCard checkCard in summonList) + { + cardData = YGOSharp.OCGWrapper.NamedCard.Get(checkCard.Id); + if (cardData != null && cardData.Attack < currentAttack) return false; + } + + return true; + } + + public bool MonsterRepos() + { + int selfAttack = Card.Attack + 1; + int extraAttackForDogmatika = 0; + if (!activatedCardIdList.Contains(CardId.DogmatikaFleurdelis + 1) && Bot.HasInMonstersZone(CardId.DogmatikaFleurdelis, true, false, true)) extraAttackForDogmatika += 500; + if (Card.HasSetcode(SetcodeDogmatika)) + { + selfAttack += extraAttackForDogmatika; + } + + if (Card.IsFaceup() && Card.IsDefense() && selfAttack <= 1) + return false; + + int bestAttack = 0; + foreach (ClientCard card in Bot.GetMonsters()) + { + int attack = card.Attack; + if (card.HasSetcode(SetcodeDogmatika)) + { + attack += extraAttackForDogmatika; + } + if (attack >= bestAttack) + { + bestAttack = attack; + } + } + + bool enemyBetter = Util.IsAllEnemyBetterThanValue(bestAttack, true); + + if (Card.IsAttack() && enemyBetter) + return true; + if (Card.IsDefense() && !enemyBetter) + return true; + return false; + } + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + if (Card.IsCode(CardId.DogmatikaPunishment) && Bot.HasInSpellZone(Card.Id)) return false; + if (Card.IsCode(CardId.SinfulSpoilsOfDoom_Rciela)) + { + if (!Bot.HasInHand(CardId.DogmatikaFleurdelis) && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.Level >= 7 && card.HasRace(CardRace.SpellCaster))) + { + return false; + } + } + + // select place + if (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) + { + List avoid_list = new List(); + int setFornfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setFornfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setFornfiniteImpermanence); + return true; + } else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } else + { + SelectSTPlace(); + } + return true; + } + + else if (Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true) || Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true)) + { + if (Card.IsSpell() && !Bot.HasInSpellZone(Card.Id)) + { + SelectSTPlace(); + return true; + } + } + + return false; + } + + protected override bool DefaultSetForDiabellze() + { + if (base.DefaultSetForDiabellze()) + { + SelectSTPlace(null, true); + return true; + } + return false; + } + } +} diff --git a/Game/AI/Decks/DragmaExecutor.cs b/Game/AI/Decks/DragmaExecutor.cs index 9f9dec53..4858f731 100644 --- a/Game/AI/Decks/DragmaExecutor.cs +++ b/Game/AI/Decks/DragmaExecutor.cs @@ -8,7 +8,7 @@ namespace WindBot.Game.AI.Decks { - [Deck("Dogmatika", "AI_Dogmatika")] + [Deck("Dogmatika", "AI_Dragma")] class DragmaExecutor : DefaultExecutor { public class CardId diff --git a/Game/AI/Decks/DragunExecutor.cs b/Game/AI/Decks/DragunExecutor.cs index e9d2cc77..b54f9a84 100644 --- a/Game/AI/Decks/DragunExecutor.cs +++ b/Game/AI/Decks/DragunExecutor.cs @@ -123,6 +123,7 @@ public override void OnNewTurn() { BeastOLionUsed = false; RedEyesFusionUsed = false; + base.OnNewTurn(); } public override CardPosition OnSelectPosition(int cardId, IList positions) @@ -198,6 +199,7 @@ private bool RedEyesFusionEffect() private bool TourGuideFromTheUnderworldSummon() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Bot.GetRemainingCount(CardId.TourGuideFromTheUnderworld, 2) == 0 && Bot.GetRemainingCount(CardId.Sangan, 2) == 0) return false; return true; @@ -307,6 +309,7 @@ private bool SummonForMaterial() private bool MagiciansSoulsEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Card.Location == CardLocation.Hand) { if (RedEyesFusionUsed) @@ -375,6 +378,7 @@ private bool MagicalizedFusionEffect() private bool PredaplantVerteAnacondaEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (ActivateDescription == Util.GetStringId(CardId.PredaplantVerteAnaconda, 0)) return false; AI.SelectCard(CardId.RedEyesFusion); diff --git a/Game/AI/Decks/DragunityExecutor.cs b/Game/AI/Decks/DragunityExecutor.cs index 6029400e..0c65e32f 100644 --- a/Game/AI/Decks/DragunityExecutor.cs +++ b/Game/AI/Decks/DragunityExecutor.cs @@ -456,6 +456,7 @@ private bool DragunityPhalanxSet() private bool AssaultBeast() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (!Bot.HasInSpellZone(CardId.AssaultModeActivate)) return true; return false; diff --git a/Game/AI/Decks/ExosisterExecutor.cs b/Game/AI/Decks/ExosisterExecutor.cs new file mode 100644 index 00000000..0da8ff6e --- /dev/null +++ b/Game/AI/Decks/ExosisterExecutor.cs @@ -0,0 +1,2990 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +using System.Linq; +using System; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Exosister", "AI_Exosister")] + + class ExosisterExecutor : DefaultExecutor + { + public class CardId + { + public const int ExosisterElis = 16474916; + public const int ExosisterStella = 43863925; + public const int ExosisterIrene = 79858629; + public const int ExosisterSophia = 5352328; + public const int ExosisterMartha = 37343995; + public const int Aratama = 16889337; + public const int Sakitama = 67972302; + // _CardId.MaxxC = 23434538; + // _CardId.AshBlossom = 14558127; + + public const int ExosisterPax = 77913594; + public const int ExosisterArment = 4408198; + public const int PotofExtravagance = 84211599; + // _CardId.CalledByTheGrave = 24224830; + + public const int ExosisterVadis = 77891946; + public const int ExosisterReturnia = 197042; + // _CardId.InfiniteImpermanence = 10045474; + + public const int ExosisterMikailis = 42741437; + public const int ExosisterKaspitell = 78135071; + public const int ExosisterGibrine = 5530780; + public const int ExosisterAsophiel = 41524885; + public const int ExosistersMagnifica = 59242457; + public const int TellarknightConstellarCaduceus = 58858807; + public const int StellarknightConstellarDiamond = 9272381; + public const int DivineArsenalAAZEUS_SkyThunder = 90448279; + public const int DonnerDaggerFurHire = 8728498; + // _CardId.EvilswarmExcitonKnight = 46772449; + + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + public const int DimensionShifter = 91800273; + } + + public ExosisterExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + // trigger + AddExecutor(ExecutorType.Activate, CardId.ExosistersMagnifica, ExosistersMagnificaActivateTrigger); + + // quick effect + AddExecutor(ExecutorType.Activate, CardId.ExosisterMikailis, ExosisterMikailisActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosistersMagnifica, ExosistersMagnificaActivateBanish); + AddExecutor(ExecutorType.Activate, CardId.ExosisterReturnia, ExosisterReturniaActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterVadis, ExosisterVadisActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, CardId.StellarknightConstellarDiamond); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, DefaultExosisterTransform); + AddExecutor(ExecutorType.Activate, CardId.ExosisterArment, ExosisterArmentActivate); + + // free chain + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + + // search + AddExecutor(ExecutorType.Activate, CardId.PotofExtravagance, PotofExtravaganceActivate); + + // field effect + AddExecutor(ExecutorType.Activate, CardId.Aratama); + AddExecutor(ExecutorType.Activate, CardId.DonnerDaggerFurHire, DonnerDaggerFurHireActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterKaspitell, ExosisterKaspitellActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterGibrine, ExosisterGibrineActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterAsophiel, ExosisterAsophielActivate); + + AddExecutor(ExecutorType.Activate, CardId.ExosisterSophia, ExosisterSophiaActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterIrene, ExosisterIreneActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterStella, ExosisterStellaActivate); + + // addition monster summmon + AddExecutor(ExecutorType.Activate, CardId.ExosisterElis, ExosisterElisActivate); + AddExecutor(ExecutorType.Activate, CardId.Sakitama, SakitamaActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterPax, ExosisterPaxActivate); + AddExecutor(ExecutorType.Activate, CardId.ExosisterStella, ExosisterStellaSecondActivate); + + // xyz summon + AddExecutor(ExecutorType.SpSummon, CardId.StellarknightConstellarDiamond); + AddExecutor(ExecutorType.SpSummon, CardId.DonnerDaggerFurHire, DonnerDaggerFurHireSpSummonCheck); + AddExecutor(ExecutorType.SpSummon, CardId.ExosisterMikailis, ExosisterMikailisAdvancedSpSummonCheck); + AddExecutor(ExecutorType.SpSummon, CardId.ExosisterKaspitell, ExosisterKaspitellAdvancedSpSummonCheck); + + AddExecutor(ExecutorType.SpSummon, CardId.ExosisterKaspitell, ExosisterKaspitellSpSummonCheck); + AddExecutor(ExecutorType.SpSummon, CardId.ExosisterMikailis, ExosisterMikailisSpSummonCheck); + AddExecutor(ExecutorType.SpSummon, CardId.TellarknightConstellarCaduceus, TellarknightConstellarCaduceusSpSummonCheck); + + AddExecutor(ExecutorType.SpSummon, CardId.ExosistersMagnifica, ExosistersMagnificaSpSummonCheck); + + AddExecutor(ExecutorType.SpSummon, _CardId.EvilswarmExcitonKnight, DefaultEvilswarmExcitonKnightSummon); + AddExecutor(ExecutorType.Activate, _CardId.EvilswarmExcitonKnight, DefaultEvilswarmExcitonKnightEffect); + + // normal summon for xyz(avoiding MaxxC) + AddExecutor(ExecutorType.Summon, CardId.ExosisterStella, ExosisterAvoidMaxxCSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.ExosisterSophia, ExosisterAvoidMaxxCSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.ExosisterIrene, ExosisterAvoidMaxxCSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.ExosisterElis, ExosisterAvoidMaxxCSummonCheck); + + // activate martha + AddExecutor(ExecutorType.Activate, CardId.ExosisterMartha, ExosisterMarthaActivate); + + // normal summon for xyz + AddExecutor(ExecutorType.Summon, CardId.ExosisterStella, ExosisterStellaSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.Aratama, AratamaSummonCheck); + AddExecutor(ExecutorType.Summon, ExosisterForElisSummonCheck); + AddExecutor(ExecutorType.Summon, ForSakitamaSummonCheck); + AddExecutor(ExecutorType.Summon, CardId.ExosisterIrene, ExosisterIreneSummonCheck); + AddExecutor(ExecutorType.Summon, Level4SummonCheck); + AddExecutor(ExecutorType.Summon, ExosisterForArmentSummonCheck); + AddExecutor(ExecutorType.Summon, ForDonnerSummonCheck); + + AddExecutor(ExecutorType.Activate, CardId.ExosisterPax, ExosisterPaxActivateForEndSearch); + + // other + AddExecutor(ExecutorType.Repos, DefaultMonsterRepos); + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + } + + const int SetcodeTimeLord = 0x4a; + const int SetcodeShadoll = 0x9d; + const int SetcodeInferoid = 0xbb; + const int SetcodeOrcust = 0x11b; + const int SetcodeExosister = 0x174; + const int SetcodeTearlaments = 0x182; + List SetcodeForDiamond = new List{SetcodeShadoll, SetcodeInferoid, SetcodeTearlaments}; + + List affectGraveCardIdList = new List{ + 71344451, 40975243, 87746184, 70534340, 45906428, 71490127, 3659803, 12071500, 6077601, 11827244, 95238394, 81223446, 40003819, + 72490637, 21011044, 59419719, 14735698, 45410988 + }; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.ExosisterElis, CardId.ExosisterStella, CardId.ExosisterMartha, CardId.Aratama, CardId.Sakitama, + _CardId.MaxxC, _CardId.AshBlossom, CardId.ExosisterPax, CardId.ExosisterVadis }}, + {2, new List { CardId.ExosisterIrene, CardId.ExosisterSophia, CardId.PotofExtravagance, _CardId.CalledByTheGrave, + CardId.ExosisterReturnia, _CardId.InfiniteImpermanence }}, + {1, new List { CardId.ExosisterArment }}, + }; + Dictionary ExosisterMentionTable = new Dictionary{ + {CardId.ExosisterElis, CardId.ExosisterStella}, {CardId.ExosisterStella, CardId.ExosisterElis}, + {CardId.ExosisterIrene, CardId.ExosisterSophia}, {CardId.ExosisterSophia, CardId.ExosisterIrene}, + {CardId.ExosisterMartha, CardId.ExosisterElis} + }; + List ExosisterSpellTrapList = new List{CardId.ExosisterPax, CardId.ExosisterArment, CardId.ExosisterVadis, CardId.ExosisterReturnia}; + + List currentNegatingIdList = new List(); + bool enemyActivateMaxxC = false; + bool enemyActivateLockBird = false; + bool enemyMoveGrave = false; + bool paxCallToField = false; + List infiniteImpermanenceList = new List(); + + bool summoned = false; + bool elisEffect1Activated = false; + bool stellaEffect1Activated = false; + bool irenaEffect1Activated = false; + bool sophiaEffect1Activated = false; + bool marthaEffect1Activated = false; + bool mikailisEffect1Activated = false; + bool mikailisEffect3Activated = false; + bool kaspitellEffect1Activated = false; + bool kaspitellEffect3Activated = false; + bool gibrineEffect1Activated = false; + bool gibrineEffect3Activated = false; + bool asophielEffect1Activated = false; + bool asophielEffect3Activated = false; + bool sakitamaEffect1Activated = false; + List exosisterTransformEffectList = new List(); + List oncePerTurnEffectActivatedList = new List(); + List activatedMagnificaList = new List(); + List targetedMagnificaList = new List(); + List transformDestList = new List(); + List spSummonThisTurn = new List(); + bool potActivate = false; + List removeChosenList = new List(); + + /// + /// Shuffle List and return a random-order card list + /// + public List ShuffleCardList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(n + 1); + ClientCard temp = result[index]; + result[index] = result[n]; + result[n] = temp; + } + return result; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false) + { + List floodagateList = Enemy.GetMonsters().Where(c => c?.Data != null && + c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (floodagateList.Count > 0) + { + floodagateList.Sort(CardContainer.CompareCardAttack); + floodagateList.Reverse(); + return floodagateList[0]; + } + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterDangerous() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (dangerList.Count > 0) + { + dangerList.Sort(CardContainer.CompareCardAttack); + dangerList.Reverse(); + return dangerList[0]; + } + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterInvincible() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (invincibleList.Count > 0) + { + invincibleList.Sort(CardContainer.CompareCardAttack); + invincibleList.Reverse(); + return invincibleList[0]; + } + + if (attack == 0) + attack = Util.GetBestAttack(Bot); + List betterList = Enemy.MonsterZone.GetMonsters() + .Where(card => card.GetDefensePower() >= attack && card.IsAttack() && (!canBeTarget || !card.IsShouldNotBeTarget())).ToList(); + if (betterList.Count > 0) + { + betterList.Sort(CardContainer.CompareCardAttack); + betterList.Reverse(); + return betterList[0]; + } + return null; + } + + public ClientCard GetProblematicEnemyCard(bool canBeTarget = false) + { + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !removeChosenList.Contains(c) && + c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (floodagateList.Count > 0) + { + floodagateList.Sort(CardContainer.CompareCardAttack); + floodagateList.Reverse(); + return floodagateList[0]; + } + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !removeChosenList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count > 0) + { + return ShuffleCardList(problemEnemySpellList)[0]; + } + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !removeChosenList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (dangerList.Count > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) + { + dangerList.Sort(CardContainer.CompareCardAttack); + dangerList.Reverse(); + return dangerList[0]; + } + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !removeChosenList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (invincibleList.Count > 0) + { + invincibleList.Sort(CardContainer.CompareCardAttack); + invincibleList.Reverse(); + return invincibleList[0]; + } + + List enemyMonsters = Enemy.GetMonsters().Where(c => !removeChosenList.Contains(c)).ToList(); + if (enemyMonsters.Count > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach(ClientCard target in enemyMonsters) + { + if (target.HasType(CardType.Fusion) || target.HasType(CardType.Ritual) || target.HasType(CardType.Synchro) || target.HasType(CardType.Xyz) || (target.HasType(CardType.Link) && target.LinkCount >= 2) ) + { + if (!canBeTarget || !(target.IsShouldNotBeTarget() || target.IsShouldNotBeMonsterTarget())) return target; + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !removeChosenList.Contains(c) + && (c.HasType(CardType.Equip) || c.HasType(CardType.Pendulum) || c.HasType(CardType.Field) || c.HasType(CardType.Continuous))) + .ToList(); + if (spells.Count > 0) + { + return ShuffleCardList(spells)[0]; + } + + return null; + } + + public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false) + { + ClientCard card = GetProblematicEnemyMonster(0, canBeTarget); + if (card != null) + return card; + + card = Enemy.MonsterZone.GetHighestAttackMonster(canBeTarget); + if (card != null) + return card; + + List monsters = Enemy.GetMonsters(); + + // after GetHighestAttackMonster, the left monsters must be face-down. + if (monsters.Count > 0 && !onlyFaceup) + return ShuffleCardList(monsters)[0]; + + return null; + } + + public ClientCard GetBestEnemySpell(bool onlyFaceup = false, bool canBeTarget = false) + { + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count > 0) + { + return ShuffleCardList(problemEnemySpellList)[0]; + } + + List spells = Enemy.GetSpells().Where(card => !(card.IsFaceup() && card.IsCode(_CardId.EvenlyMatched))).ToList(); + + List faceUpList = spells.Where(ecard => ecard.IsFaceup() && + (ecard.HasType(CardType.Continuous) || ecard.HasType(CardType.Field) || ecard.HasType(CardType.Pendulum))).ToList(); + if (faceUpList.Count > 0) + { + return ShuffleCardList(faceUpList)[0]; + } + + if (spells.Count > 0 && !onlyFaceup) + { + return ShuffleCardList(spells)[0]; + } + + return null; + } + + public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false, bool checkGrave = false) + { + ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget); + if (card != null) + { + return card; + } + + card = GetBestEnemySpell(onlyFaceup, canBeTarget); + if (card != null) + { + return card; + } + + if (checkGrave && Enemy.Graveyard.Count > 0) + { + List graveMonsterList = Enemy.Graveyard.GetMatchingCards(c => c.IsMonster()).ToList(); + if (graveMonsterList.Count > 0) + { + graveMonsterList.Sort(CardContainer.CompareCardAttack); + graveMonsterList.Reverse(); + return graveMonsterList[0]; + } + return ShuffleCardList(Enemy.Graveyard.ToList())[0]; + } + + return null; + } + + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + + /// + /// Check negated turn count of id + /// + public int CheckCalledbytheGrave(int id) + { + if (currentNegatingIdList.Contains(id)) return 1; + if (DefaultCheckWhetherCardIdIsNegated(id)) return 1; + return 0; + } + + public void CheckEnemyMoveGrave() + { + if (Duel.LastChainPlayer == 1) + { + ClientCard card = Util.GetLastChainCard(); + if (Duel.LastChainLocation == CardLocation.Grave && card.Location == CardLocation.Grave) + { + Logger.DebugWriteLine("===Exosister: enemy activate effect from GY."); + enemyMoveGrave = true; + } + else if (affectGraveCardIdList.Contains(card.Id)) + { + Logger.DebugWriteLine("===Exosister: enemy activate effect that affect GY."); + enemyMoveGrave = true; + } + else + { + foreach (ClientCard targetCard in Duel.LastChainTargets) + { + if (targetCard.Location == CardLocation.Grave) + { + Logger.DebugWriteLine("===Exosister: enemy target cards of GY."); + enemyMoveGrave = true; + break; + } + } + } + } + } + + /// + /// Check exosister's relative card. 0 for error. + /// + public int CheckExosisterMentionCard(int id) + { + if (!ExosisterMentionTable.ContainsKey(id)) + { + return 0; + } + return ExosisterMentionTable[id]; + } + + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + return true; + } + + /// + /// Check whether opposite use Maxx-C, and thus make less operation. + /// + public bool CheckLessOperation() + { + if (!enemyActivateMaxxC) + { + return false; + } + return CheckAtAdvantage(); + } + + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && Bot.GetMonsters().Any(card => card.IsFaceup())) + { + return true; + } + return false; + } + + /// + /// Check whether bot is in danger and need to summon monster to defense. + /// + public bool CheckInDanger() + { + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + int totalAtk = 0; + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsAttack() && !m.Attacked) totalAtk += m.Attack; + } + if (totalAtk >= Bot.LifePoints) return true; + } + return false; + } + + /// + /// Check whether can be used for xyz summon. + /// + public bool CheckAbleForXyz(ClientCard card) + { + return card.IsFaceup() && !card.HasType(CardType.Xyz) && !card.HasType(CardType.Link) && !card.HasType(CardType.Token) && card.Level == 4; + } + + /// + /// Check whether bot can activate martha. + /// + public bool CheckMarthaActivatable() + { + return !marthaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterMartha) == 0 && CheckRemainInDeck(CardId.ExosisterElis) > 0 + && !Bot.GetMonsters().Any(card => card.IsFacedown() || !card.HasType(CardType.Xyz)); + } + + /// + /// check enemy's dangerous card in grave + /// + public List CheckDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && card.HasSetcode(SetcodeOrcust)).ToList(); + return result; + } + + /// + /// Whether spell or trap will be negate. If so, return true. + /// + /// is counter trap + /// check target + /// + public bool SpellNegatable(bool isCounter = false, ClientCard target = null) + { + // target default set + if (target == null) target = Card; + // won't negate if not on field + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + // negate judge + if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap()) + { + if (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true)) return true; + } + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) { + return true; + } + } + // how to get here? + return false; + } + + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated(bool disablecheck = true){ + if (Card.IsSpell() || Card.IsTrap()){ + if (SpellNegatable()) return true; + } + if (CheckCalledbytheGrave(Card.Id) > 0){ + return true; + } + if (Card.IsMonster() && Card.Location == CardLocation.MonsterZone && Card.IsDefense()) + { + if (Enemy.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled()) != null + || Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled()) != null) + { + return true; + } + } + if (disablecheck){ + return Card.IsDisabled(); + } + return false; + } + + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public void SelectXyzMaterial(int num = 2, bool needExosister = false) + { + List materialList = Bot.GetMonsters().Where(card => CheckAbleForXyz(card)).ToList(); + if (materialList?.Count() < num) + { + return; + } + if (needExosister && !materialList.Any(card => card.HasSetcode(SetcodeExosister))) + { + return; + } + List selectedList = new List(); + + // if needed, select exosister with less atk first + if (needExosister) + { + List exosisterList = materialList.Where(card => card.HasSetcode(SetcodeExosister)).ToList(); + exosisterList.Sort(CardContainer.CompareCardAttack); + ClientCard firstSelect = exosisterList[0]; + selectedList.Add(firstSelect); + materialList.Remove(firstSelect); + } + + // select non-exosister or effecte used's exosister first + // never use martha first + List sortMaterialList = materialList.Where(card => + (card?.Data != null && !card.HasSetcode(SetcodeExosister)) || (exosisterTransformEffectList.Contains(card.Id) && card.Id != CardId.ExosisterMartha)).ToList(); + sortMaterialList.Sort(CardContainer.CompareCardAttack); + foreach (ClientCard card in sortMaterialList) + { + selectedList.Add(card); + if (selectedList.Count() >= num) + { + AI.SelectMaterials(selectedList); + return; + } + } + + List valuableMaterialList = materialList.Where(card => card.Id == CardId.ExosisterMartha || !exosisterTransformEffectList.Contains(card.Id)).ToList(); + valuableMaterialList.Sort(CardContainer.CompareCardAttack); + foreach (ClientCard card in valuableMaterialList) + { + selectedList.Add(card); + if (selectedList.Count() >= num) + { + AI.SelectMaterials(selectedList); + return; + } + } + } + + public void SelectDetachMaterial(ClientCard activateCard) + { + // TODO + AI.SelectCard(0); + } + + /// + /// go first + /// + public override bool OnSelectHand() + { + return true; + } + + /// + /// check whether enemy activate important card + /// + public override void OnChaining(int player, ClientCard card) + { + if (card == null) return; + + if (player == 1) + { + if (card.IsCode(_CardId.MaxxC) && CheckCalledbytheGrave(_CardId.MaxxC) == 0) + { + enemyActivateMaxxC = true; + } + if (card.IsCode(_CardId.LockBird) && CheckCalledbytheGrave(_CardId.LockBird) == 0) + { + enemyActivateLockBird = true; + } + if (card.IsCode(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + infiniteImpermanenceList.Add(4-i); + break; + } + } + } + if (Duel.LastChainLocation == CardLocation.Grave && card.Location == CardLocation.Grave) + { + Logger.DebugWriteLine("===Exosister: enemy activate effect from GY."); + enemyMoveGrave = true; + } + } + base.OnChaining(player, card); + } + + public override void OnSelectChain(IList cards) + { + int player = Duel.LastChainPlayer; + ClientCard card = Util.GetLastChainCard(); + if (player == 1) + { + foreach (ClientCard targetCard in Duel.LastChainTargets) { + if (targetCard.Location == CardLocation.Grave) + { + Logger.DebugWriteLine("===Exosister: enemy target cards of GY."); + enemyMoveGrave = true; + break; + } + } + } + base.OnSelectChain(cards); + } + + public override void OnChainSolved(int chainIndex) + { + ChainInfo currentCard = Duel.GetCurrentSolvingChainInfo(); + if (currentCard != null && !Duel.IsCurrentSolvingChainNegated() && currentCard.ActivatePlayer == 1) + { + if (currentCard.IsCode(_CardId.MaxxC)) + enemyActivateMaxxC = true; + if (currentCard.IsCode(_CardId.LockBird)) + enemyActivateLockBird = true; + if (currentCard.IsCode(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == currentCard.RelatedCard) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + } + } + + + /// + /// clear chain information + /// + public override void OnChainEnd() + { + enemyMoveGrave = false; + paxCallToField = false; + potActivate = false; + transformDestList.Clear(); + targetedMagnificaList.Clear(); + if (activatedMagnificaList.Count() > 0) + { + for (int idx = activatedMagnificaList.Count() - 1; idx >= 0; -- idx) + { + ClientCard checkTarget = activatedMagnificaList[idx]; + if (checkTarget == null || checkTarget.IsFacedown() || checkTarget.Location != CardLocation.MonsterZone) + { + activatedMagnificaList.RemoveAt(idx); + } + } + } + if (spSummonThisTurn.Count() > 0) + { + for (int idx = spSummonThisTurn.Count() - 1; idx >= 0; -- idx) + { + ClientCard checkTarget = spSummonThisTurn[idx]; + if (checkTarget == null || checkTarget.IsFacedown() || checkTarget.Location != CardLocation.MonsterZone) + { + spSummonThisTurn.RemoveAt(idx); + } + } + } + base.OnChainEnd(); + } + + public override void OnNewTurn() + { + enemyActivateMaxxC = false; + enemyActivateLockBird = false; + infiniteImpermanenceList.Clear(); + currentNegatingIdList.Clear(); + + summoned = false; + elisEffect1Activated = false; + stellaEffect1Activated = false; + irenaEffect1Activated = false; + sophiaEffect1Activated = false; + marthaEffect1Activated = false; + mikailisEffect1Activated = false; + mikailisEffect3Activated = false; + kaspitellEffect1Activated = false; + kaspitellEffect3Activated = false; + gibrineEffect1Activated = false; + gibrineEffect3Activated = false; + asophielEffect1Activated = false; + asophielEffect3Activated = false; + sakitamaEffect1Activated = false; + exosisterTransformEffectList.Clear(); + oncePerTurnEffectActivatedList.Clear(); + activatedMagnificaList.Clear(); + spSummonThisTurn.Clear(); + } + + /// + /// override for exosister's transform + /// + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + bool beginTransformCheck = false; + // transform for main monster + if (hint == HintMsg.SpSummon && min == 1 && max == 1 && transformDestList.Count() > 0) + { + // check whether for transform + if (cards.All(card => card.Location == CardLocation.Extra && card.Rank == 4 && card.HasSetcode(SetcodeExosister))) + { + beginTransformCheck = true; + } + } + // transform for magnifica + if (hint == HintMsg.ToDeck && min == 1 && max == 1 && transformDestList.Count() > 0) + { + if (cards.All(card => card.Location == CardLocation.Overlay)) + { + beginTransformCheck = true; + } + } + if (beginTransformCheck) + { + for (int idx = 0; idx < transformDestList.Count(); ++ idx) + { + int targetId = transformDestList[idx]; + ClientCard targetCard = cards.FirstOrDefault(card => card.IsCode(targetId)); + if (targetCard != null) + { + List result = new List(); + result.Add(targetCard); + transformDestList.RemoveAt(idx); + spSummonThisTurn.AddRange(result); + return Util.CheckSelectCount(result, cards, min, max); + } + } + } + + if (Util.ChainContainsCard(_CardId.EvenlyMatched) && Util.ChainContainPlayer(1) && hint == HintMsg.Remove) + { + int botCount = Bot.GetMonsterCount() + Bot.GetSpellCount(); + int oppositeCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); + if (botCount - oppositeCount == min && min == max) + { + Logger.DebugWriteLine("===Exosister: Evenly Matched activated."); + List allBotCards = new List(); + allBotCards.AddRange(Bot.GetMonsters()); + allBotCards.AddRange(Bot.GetSpells()); + List importantList = new List(); + + List magnificaList = allBotCards.Where(card => card.IsCode(CardId.ExosistersMagnifica)).ToList(); + if (magnificaList.Count > 0) + { + allBotCards.RemoveAll(c => magnificaList.Contains(c)); + importantList.AddRange(magnificaList); + } + if (!mikailisEffect1Activated) + { + List mikailisList = allBotCards.Where(card => spSummonThisTurn.Contains(card) + && card.IsCode(CardId.ExosisterMikailis) && card.IsFaceup()).ToList(); + if (mikailisList.Count > 0) + { + allBotCards.RemoveAll(c => mikailisList.Contains(c)); + importantList.AddRange(mikailisList); + } + } + if (!gibrineEffect1Activated) + { + List gibrineList = allBotCards.Where(card => spSummonThisTurn.Contains(card) + && card.IsCode(CardId.ExosisterGibrine) && card.IsFaceup()).ToList(); + if (gibrineList.Count > 0) + { + allBotCards.RemoveAll(c => gibrineList.Contains(c)); + importantList.AddRange(gibrineList); + } + } + if (!oncePerTurnEffectActivatedList.Contains(CardId.ExosisterVadis)) + { + List vadisList = allBotCards.Where(card => card.IsCode(CardId.ExosisterVadis) && card.IsFacedown()).ToList(); + if (vadisList.Count > 0) + { + allBotCards.RemoveAll(c => vadisList.Contains(c)); + importantList.AddRange(vadisList); + } + } + List xyzList = allBotCards.Where(card => card.IsMonster() && card.HasType(CardType.Xyz)).ToList(); + if (xyzList.Count > 0) + { + xyzList.Sort(CardContainer.CompareCardAttack); + xyzList.Reverse(); + allBotCards.RemoveAll(c => xyzList.Contains(c)); + importantList.AddRange(xyzList); + } + List monsterList = allBotCards.Where(card => card.IsMonster()).ToList(); + if (monsterList.Count > 0) + { + monsterList.Sort(CardContainer.CompareCardAttack); + monsterList.Reverse(); + allBotCards.RemoveAll(c => monsterList.Contains(c)); + importantList.AddRange(monsterList); + } + List faceDownList = allBotCards.Where(card => card.IsFacedown()).ToList(); + if (faceDownList.Count > 0) + { + allBotCards.RemoveAll(c => faceDownList.Contains(c)); + importantList.AddRange(ShuffleCardList(faceDownList)); + } + + importantList.Reverse(); + return Util.CheckSelectCount(importantList, cards, min, max); + } + } + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Util.IsTurn1OrMain2()) + { + bool turnDefense = false; + if (cardId == CardId.DivineArsenalAAZEUS_SkyThunder || cardId == CardId.ExosistersMagnifica) + { + turnDefense = true; + } + if (!cardData.HasType(CardType.Xyz)) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (!cardData.HasType(CardType.Xyz) || cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardData.Attack); + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + /// + /// override for magnifica's spsummon + /// + public override bool OnSelectYesNo(long desc) + { + // magnifica spsummon + if (desc == Util.GetStringId(CardId.ExosistersMagnifica, 2)) + { + return true; + } + // pax spsummon + if (desc == Util.GetStringId(CardId.ExosisterPax, 1)) + { + return paxCallToField; + } + + return base.OnSelectYesNo(desc); + } + + /// + /// override for returnia's option + /// + public override int OnSelectOption(IList options) + { + // check retunia + int spSummonOption = -1; + int banishOption = -1; + int doNothingOption = -1; + for (int idx = 0; idx < options.Count(); ++ idx) + { + long option = options[idx]; + if (option == Util.GetStringId(CardId.ExosisterReturnia, 0)) + { + spSummonOption = idx; + } else if (option == Util.GetStringId(CardId.ExosisterReturnia, 1)) + { + banishOption = idx; + } else if (option == Util.GetStringId(CardId.ExosisterReturnia, 2)) + { + doNothingOption = idx; + } + } + + if (spSummonOption >= 0 || banishOption >= 0 || doNothingOption >= 0) + { + if (spSummonOption < 0 && banishOption < 0) + { + return doNothingOption; + } + if (banishOption >= 0) + { + // banish problem card + ClientCard target = GetProblematicEnemyCard(true); + if (target != null) + { + AI.SelectCard(target); + return banishOption; + } + + // dump banish + target = GetBestEnemyCard(false, false); + if (target != null) + { + AI.SelectCard(target); + return banishOption; + } + } + if (spSummonOption >= 0) + { + // TODO + } + } + + // check pot + int potBanish6Option = -1; + int potBanish3Option = -1; + for (int idx = 0; idx < options.Count(); ++idx) + { + long option = options[idx]; + if (option == Util.GetStringId(CardId.PotofExtravagance, 0)) + { + potBanish3Option = idx; + } + else if (option == Util.GetStringId(CardId.PotofExtravagance, 1)) + { + potBanish6Option = idx; + } + } + if (potBanish3Option >= 0 || potBanish6Option >= 0) + { + if (Bot.ExtraDeck.Count() > 9 && potBanish6Option >= 0) + { + return potBanish6Option; + } + return potBanish3Option; + } + + return base.OnSelectOption(options); + } + + public bool AshBlossomActivate() + { + if (CheckWhetherNegated(true) || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + if (CheckAtAdvantage()) + { + return false; + } + } + return DefaultAshBlossomAndJoyousSpring(); + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + // negate before effect used + foreach(ClientCard m in Enemy.GetMonsters()) + { + if (m.IsMonsterShouldBeDisabledBeforeItUseEffect() && !m.IsDisabled() && Duel.LastChainPlayer != 0) + { + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++ i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(m); + return true; + } + } + + ClientCard LastChainCard = Util.GetLastChainCard(); + + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ( (this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || (Util.IsChainTarget(Card)) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + List enemyMonsters = Enemy.GetMonsters(); + AI.SelectCard(target); + infiniteImpermanenceList.Add(this_seq); + return true; + } + } + if ( (LastChainCard == null || LastChainCard.Controller != 1 || LastChainCard.Location != CardLocation.MonsterZone + || LastChainCard.IsDisabled() || LastChainCard.IsShouldNotBeTarget() || LastChainCard.IsShouldNotBeSpellTrapTarget()) ) + return false; + // negate monsters + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + if (LastChainCard != null) AI.SelectCard(LastChainCard); + else + { + List enemyMonsters = Enemy.GetMonsters(); + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach (ClientCard card in enemyMonsters) + { + if (card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + { + AI.SelectCard(card); + return true; + } + } + } + return true; + } + + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated(true)) return false; + if (Duel.LastChainPlayer == 1) + { + // negate + if (Util.GetLastChainCard().IsMonster()) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + if (CheckCalledbytheGrave(code) > 0) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC) && CheckAtAdvantage()) + { + return false; + } + if (code == CardId.DimensionShifter) + { + return false; + } + ClientCard targetCard = Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.IsOriginalCode(code)); + if (targetCard != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(targetCard); + currentNegatingIdList.Add(code); + return true; + } + } + + // banish target + foreach (ClientCard cards in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(cards)) + { + int code = cards.Id; + AI.SelectCard(cards); + currentNegatingIdList.Add(code); + return true; + } + } + + // become targets + if (Duel.ChainTargets.Contains(Card)) + { + List enemyMonsters = Enemy.Graveyard.GetMatchingCards(card => card.IsMonster()).ToList(); + if (enemyMonsters.Count > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + int code = enemyMonsters[0].Id; + AI.SelectCard(code); + currentNegatingIdList.Add(code); + return true; + } + } + } + + // avoid danger monster in grave + if (Duel.LastChainPlayer == 1) return false; + List targets = CheckDangerousCardinEnemyGrave(true); + if (targets.Count() > 0) { + int code = targets[0].GetOriginCode(); + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(targets); + currentNegatingIdList.Add(code); + return true; + } + + return false; + } + + public List GetPotofExtravaganceBanish() + { + List banishList = new List(); + ClientCard aaZeus = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.DivineArsenalAAZEUS_SkyThunder)); + if (aaZeus != null) + { + banishList.Add(aaZeus); + } + + ClientCard diamond = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.StellarknightConstellarDiamond)); + if (diamond != null) + { + banishList.Add(diamond); + } + + ClientCard caduceus = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.TellarknightConstellarCaduceus)); + if (caduceus != null) + { + banishList.Add(caduceus); + } + + ClientCard evilswarm = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(_CardId.EvilswarmExcitonKnight)); + if (evilswarm != null) + { + banishList.Add(evilswarm); + } + + // second asophiel + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosisterAsophiel)) > 1) + { + ClientCard asophiel2 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterAsophiel)); + banishList.Add(asophiel2); + } + + ClientCard gibrine = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterGibrine)); + if (gibrine != null) + { + banishList.Add(gibrine); + } + + // 6 done + + // third mikailis + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosisterMikailis)) > 2) + { + ClientCard mikailis3 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterMikailis)); + banishList.Add(mikailis3); + } + + // first asophiel + ClientCard asophiel1 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterAsophiel) && !banishList.Contains(card)); + if (asophiel1 != null) + { + banishList.Add(asophiel1); + } + + ClientCard donner = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.DonnerDaggerFurHire)); + if (donner != null) + { + banishList.Add(donner); + } + + // 9 done + + // second kaspitell + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosisterKaspitell)) > 1) + { + ClientCard kaspitell = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterKaspitell)); + banishList.Add(kaspitell); + } + + // second magnifica + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosistersMagnifica)) > 1) + { + ClientCard magnifica2 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosistersMagnifica)); + banishList.Add(magnifica2); + } + + // second mikailis + if (Bot.ExtraDeck.Count(card => card.IsCode(CardId.ExosisterMikailis)) > 1) + { + ClientCard mikailis2 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterMikailis) && !banishList.Contains(card)); + banishList.Add(mikailis2); + } + + // first magnifica + ClientCard magnifica1 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosistersMagnifica) && !banishList.Contains(card)); + if (magnifica1 != null) + { + banishList.Add(magnifica1); + } + + // first kaspitell + ClientCard kaspitell1 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterKaspitell) && !banishList.Contains(card)); + if (kaspitell1 != null) + { + banishList.Add(kaspitell1); + } + + // first mikailis1 + ClientCard mikailis1 = Bot.ExtraDeck.FirstOrDefault(card => card.IsCode(CardId.ExosisterMikailis) && !banishList.Contains(card)); + if (mikailis1 != null) + { + banishList.Add(mikailis1); + } + + return banishList; + } + + public bool PotofExtravaganceActivate() + { + if (CheckWhetherNegated()) + { + return false; + } + List banishList = GetPotofExtravaganceBanish(); + + List addToHandOrderList = new List(); + + bool marthaActivatable = CheckMarthaActivatable(); + if (marthaActivatable) + { + if (!Bot.HasInHand(CardId.ExosisterMartha)) + { + addToHandOrderList.Add(CardId.ExosisterMartha); + } + if (Bot.HasInHand(CardId.ExosisterMartha) && !Bot.HasInHandOrInSpellZone(_CardId.CalledByTheGrave)) + { + addToHandOrderList.Add(_CardId.CalledByTheGrave); + } + } + int exosisterCount = Bot.Hand.Count(card => card?.Data != null && card.HasSetcode(SetcodeExosister)); + if (!stellaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterStella) == 0) + { + if (!Bot.HasInHand(CardId.ExosisterStella) && exosisterCount > 0) + { + addToHandOrderList.Add(CardId.ExosisterStella); + } + if (Bot.HasInHand(CardId.ExosisterStella) && exosisterCount == 0) + { + addToHandOrderList.AddRange(new List{ + CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterStella, CardId.ExosisterMartha, CardId.ExosisterElis}); + } + } + if (exosisterCount >= 0 && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + addToHandOrderList.Add(CardId.ExosisterReturnia); + } + List remainOrderList = new List{ + CardId.Aratama, CardId.Sakitama, _CardId.MaxxC, _CardId.AshBlossom, _CardId.InfiniteImpermanence, + _CardId.CalledByTheGrave, CardId.ExosisterVadis, CardId.ExosisterReturnia, CardId.ExosisterPax + }; + addToHandOrderList.AddRange(remainOrderList); + + AI.SelectCard(banishList); + AI.SelectNextCard(addToHandOrderList); + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + + potActivate = true; + return true; + } + + public bool SakitamaActivate() + { + // summon + if (Card.Location == CardLocation.Hand) + { + // summon for xyz + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1) + { + AI.SelectCard(CardId.Aratama, CardId.Sakitama); + sakitamaEffect1Activated = true; + return true; + } + + // summon for summon donner + if (!CheckLessOperation() && Bot.HasInExtra(CardId.DonnerDaggerFurHire) && + !Bot.HasInHand(CardId.ExosisterMartha) || Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + List illegalList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz) && card.Level != 4 + && (card.Data == null || !card.HasSetcode(SetcodeExosister))).ToList(); + if (illegalList.Count() > 0) + { + if (illegalList.Count() == 1) + { + List otherMaterialList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz)).ToList(); + otherMaterialList.Sort(CardContainer.CompareCardAttack); + illegalList.AddRange(otherMaterialList); + } + if (illegalList.Count() == 1) + { + Logger.DebugWriteLine("===Exosister: activate sakitama for donner"); + AI.SelectCard(CardId.Aratama, CardId.Sakitama); + sakitamaEffect1Activated = true; + return true; + } + } + } + return false; + } + // add to hand + if (Card.Location == CardLocation.Grave) + { + AI.SelectCard(CardId.Sakitama, CardId.Aratama); + return true; + } + return true; + } + + public bool DonnerDaggerFurHireActivate() + { + if (CheckAtAdvantage() && !Bot.HasInHand(CardId.ExosisterMartha) && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + return false; + } + + ClientCard targetCard = Util.GetProblematicEnemyMonster(canBeTarget: true); + if (targetCard == null) + { + List enemyMonsters = Enemy.GetMonsters(); + if (enemyMonsters.Count() > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + targetCard = enemyMonsters[0]; + } + } + + if (targetCard != null) + { + AI.SelectCard(Card); + AI.SelectNextCard(targetCard); + return true; + } + + return false; + } + + public bool ExosisterElisActivate() + { + if (ActivateDescription != Util.GetStringId(CardId.ExosisterElis, 0)) + { + return false; + } + + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1) + { + elisEffect1Activated = true; + return true; + } + + return false; + } + + public bool ExosisterStellaActivate() + { + return ExosisterStellaActivateInner(true); + } + + public bool ExosisterStellaSecondActivate() + { + return ExosisterStellaActivateInner(false); + } + + public bool ExosisterStellaActivateInner(bool checkMartha = false) + { + if (ActivateDescription != Util.GetStringId(CardId.ExosisterStella, 0) || CheckWhetherNegated(true)) + { + return false; + } + + bool ableToXyz = Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) >= 2; + + if (CheckLessOperation() && ableToXyz) + { + return false; + } + if (checkMartha && Bot.HasInHand(CardId.ExosisterMartha) && ableToXyz + && Bot.Hand.Count(card => card.IsMonster() && card.HasSetcode(CardId.ExosisterMartha)) == 1) + { + return false; + } + + AI.SelectCard(CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterStella, CardId.ExosisterElis); + stellaEffect1Activated = true; + return true; + } + + public bool ExosisterIreneActivate() + { + if (ActivateDescription != Util.GetStringId(CardId.ExosisterIrene, 0) || CheckWhetherNegated(true)) + { + return false; + } + + List shuffleList = new List(); + foreach (int cardId in new List{CardId.ExosisterIrene, CardId.ExosisterSophia, CardId.ExosisterArment}) + { + if (Bot.HasInHand(cardId)) + { + shuffleList.Add(cardId); + } + } + if (elisEffect1Activated || Bot.Hand.Count(card => card.IsCode(CardId.ExosisterElis)) > 1) + { + shuffleList.Add(CardId.ExosisterElis); + } + foreach (int cardId in new List{CardId.ExosisterPax, CardId.ExosisterReturnia, CardId.ExosisterVadis}) + { + if ((oncePerTurnEffectActivatedList.Contains(cardId) && Bot.HasInHand(cardId)) || Bot.Hand.Count(card => card.IsCode(cardId)) > 1) + { + shuffleList.Add(cardId); + } + } + + if (shuffleList.Count() > 0) + { + Logger.DebugWriteLine("===Exosister: irene return " + shuffleList[0]); + AI.SelectCard(shuffleList); + return true; + } + return false; + } + + public bool ExosisterSophiaActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.ExosisterSophia, 0) && !CheckWhetherNegated(true)) + { + sophiaEffect1Activated = true; + return true; + } + return false; + } + + public bool ExosisterMarthaActivate() + { + if (ActivateDescription != Util.GetStringId(CardId.ExosisterMartha, 0)) { + return false; + } + if (CheckLessOperation() && Bot.GetMonsterCount() > 0) + { + return false; + } + + marthaEffect1Activated = true; + return true; + } + + public bool DefaultExosisterTransform() + { + List canTransformList = new List + { + CardId.ExosisterElis, CardId.ExosisterStella, CardId.ExosisterIrene, CardId.ExosisterSophia, CardId.ExosisterMartha + }; + if (Card.IsDisabled() || !canTransformList.Contains(Card.Id)) + { + return false; + } + List checkTransformCode = new List{ + Util.GetStringId(CardId.ExosisterElis, 1), + Util.GetStringId(CardId.ExosisterStella, 1), + Util.GetStringId(CardId.ExosisterIrene, 1), + Util.GetStringId(CardId.ExosisterSophia, 1), + Util.GetStringId(CardId.ExosisterMartha, 1) + }; + if (!checkTransformCode.Contains((int)ActivateDescription) && ActivateDescription != -1) + { + return false; + } + + // mikailis + if (!Bot.HasInMonstersZone(CardId.ExosisterMikailis) && !mikailisEffect1Activated && (Duel.Player == 1 || !mikailisEffect3Activated) + && !transformDestList.Contains(CardId.ExosisterMikailis) && Bot.HasInExtra(CardId.ExosisterMikailis)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterMikailis); + return true; + } + + // kaspitell on bot's turn + if (!Bot.HasInMonstersZone(CardId.ExosisterKaspitell) && !kaspitellEffect3Activated && Duel.Player == 0 + && !transformDestList.Contains(CardId.ExosisterKaspitell) && Bot.HasInExtra(CardId.ExosisterKaspitell)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterKaspitell); + return true; + } + + // gibrine + if (!Bot.HasInMonstersZone(CardId.ExosisterGibrine) && !gibrineEffect1Activated + && !transformDestList.Contains(CardId.ExosisterGibrine) && Bot.HasInExtra(CardId.ExosisterGibrine)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterGibrine); + return true; + } + + // asophiel + if (!Bot.HasInMonstersZone(CardId.ExosisterAsophiel) && !asophielEffect1Activated + && !transformDestList.Contains(CardId.ExosisterAsophiel) && Bot.HasInExtra(CardId.ExosisterAsophiel)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterAsophiel); + return true; + } + + // kaspitell on bot's turn + if (!Bot.HasInMonstersZone(CardId.ExosisterKaspitell) && !kaspitellEffect1Activated + && !transformDestList.Contains(CardId.ExosisterKaspitell) && Bot.HasInExtra(CardId.ExosisterKaspitell)) + { + exosisterTransformEffectList.Add(Card.Id); + transformDestList.Add(CardId.ExosisterKaspitell); + return true; + } + + return false; + } + + public bool ExosisterMikailisActivate() + { + // banish + if (ActivateDescription == Util.GetStringId(CardId.ExosisterMikailis, 0)) + { + // activate after search + if (Duel.Player == 0 && !mikailisEffect3Activated && Duel.Phase < DuelPhase.End && !DefaultOnBecomeTarget()) + { + return false; + } + + // banish problem card + ClientCard target = GetProblematicEnemyCard(true); + if (target != null && Duel.LastChainPlayer != 0) + { + removeChosenList.Add(target); + mikailisEffect1Activated = true; + AI.SelectCard(target); + return true; + } + + // banish target + if (Duel.LastChainPlayer == 1) + { + List targetList = Duel.LastChainTargets.Where(card => card.Controller == 1 && + (card.Location == CardLocation.Grave || card.Location == CardLocation.MonsterZone || card.Location == CardLocation.SpellZone || card.Location == CardLocation.FieldZone)).ToList(); + if (targetList.Count() > 0) + { + mikailisEffect1Activated = true; + AI.SelectCard(ShuffleCardList(targetList)); + return true; + } + } + + // dump banish + target = GetBestEnemyCard(false, true, true); + if ((DefaultOnBecomeTarget() && !Util.ChainContainsCard(_CardId.EvenlyMatched)) || Bot.UnderAttack || (Duel.Phase == DuelPhase.End && Duel.LastChainPlayer != 0) + || (Duel.Player == 0 && Bot.GetMonsters().Count(card => card.HasType(CardType.Xyz) && card.Rank == 4 && card.HasSetcode(SetcodeExosister)) == 2 && Duel.LastChainPlayer != 0) + || (Duel.Player == 1 && Enemy.GetMonsterCount() >= 2)) + { + mikailisEffect1Activated = true; + AI.SelectCard(target); + return true; + } + return false; + } + + // search + if (CheckWhetherNegated(true)) + { + return false; + } + + List searchTarget = new List{ + CardId.ExosisterReturnia, + CardId.ExosisterVadis, + CardId.ExosisterPax, + CardId.ExosisterArment + }; + List firstSearchList = new List(); + List lastSearchList = new List(); + foreach (int cardId in searchTarget) + { + if (Bot.HasInHandOrInSpellZone(cardId) || CheckRemainInDeck(cardId) == 0) + { + lastSearchList.Add(cardId); + continue; + } + if (cardId == CardId.ExosisterReturnia && Bot.GetMonsters().Any(card => card.IsFacedown() || !card.HasSetcode(SetcodeExosister))) + { + lastSearchList.Add(cardId); + continue; + } + firstSearchList.Add(cardId); + } + firstSearchList.AddRange(lastSearchList); + + mikailisEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(firstSearchList); + return true; + } + + public bool ExosisterKaspitellActivate() + { + // block spsummon from GY + if (ActivateDescription == Util.GetStringId(CardId.ExosisterKaspitell, 0) || ActivateDescription == -1) + { + if (Enemy.HasInMonstersZone(CardId.InspectorBoarder, true)) + { + return false; + } + kaspitellEffect1Activated = true; + return true; + } + + // search + if (CheckWhetherNegated(true)) + { + return false; + } + + // search martha for activate + if (CheckMarthaActivatable() && CheckRemainInDeck(CardId.ExosisterMartha) > 0 && !Bot.HasInHand(CardId.ExosisterMartha)) + { + kaspitellEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(CardId.ExosisterMartha); + return true; + } + // search sophia for draw + if (!summoned && !sophiaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterSophia) == 0 && !Bot.HasInHand(CardId.ExosisterSophia) + && (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1 || (Bot.HasInHand(CardId.ExosisterElis) && !elisEffect1Activated))) + { + kaspitellEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(CardId.ExosisterSophia); + return true; + } + // search stella for next xyz + if (!summoned && !Bot.HasInHand(CardId.ExosisterStella) && !stellaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterStella) == 0 + && CheckRemainInDeck(CardId.ExosisterStella) > 0 && Bot.Hand.Any(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister))) + { + kaspitellEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(CardId.ExosisterStella); + return true; + } + kaspitellEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(CardId.ExosisterMartha, CardId.ExosisterStella, CardId.ExosisterElis, CardId.ExosisterSophia, CardId.ExosisterIrene); + return true; + } + + public bool ExosisterGibrineActivate() + { + // negate + if (ActivateDescription == Util.GetStringId(CardId.ExosisterGibrine, 0)) + { + if (Duel.Player == 1) + { + ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster(); + if (target != null) + { + gibrineEffect1Activated = true; + AI.SelectCard(target); + return true; + } + } + + ClientCard LastChainCard = Util.GetLastChainCard(); + if (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone && + !LastChainCard.IsDisabled() && !LastChainCard.IsShouldNotBeTarget() && !LastChainCard.IsShouldNotBeMonsterTarget()) + { + gibrineEffect1Activated = true; + AI.SelectCard(LastChainCard); + return true; + } + + return false; + } + + // gain atk + if (CheckWhetherNegated(true)) + { + return false; + } + gibrineEffect3Activated = true; + SelectDetachMaterial(Card); + return true; + } + + public bool ExosisterAsophielActivate() + { + // block activate from GY + if (ActivateDescription == Util.GetStringId(CardId.ExosisterAsophiel, 0) || ActivateDescription == -1) + { + if (Enemy.HasInMonstersZone(CardId.InspectorBoarder, true)) + { + return false; + } + asophielEffect1Activated = true; + return true; + } + + // return hand + if (CheckWhetherNegated(true)) + { + return false; + } + ClientCard targetCard = Util.GetProblematicEnemyMonster(0, true); + if (targetCard != null) + { + asophielEffect3Activated = true; + SelectDetachMaterial(Card); + AI.SelectNextCard(targetCard); + return true; + } + + return false; + } + + public bool ExosistersMagnificaActivateTrigger() + { + // sp summon + if (ActivateDescription == Util.GetStringId(CardId.ExosistersMagnifica, 1)) + { + // return after effect used + if (activatedMagnificaList.Contains(Card)) + { + // return to Mikailis for danger card + if (Card.Overlays.Contains(CardId.ExosisterMikailis) && !mikailisEffect1Activated) + { + ClientCard target = GetProblematicEnemyCard(true); + if (target != null && !Duel.CurrentChain.Any(card => card == Card)) + { + transformDestList.Add(CardId.ExosisterMikailis); + return true; + } + } + + // negate important card + if (Card.Overlays.Contains(CardId.ExosisterGibrine) && !gibrineEffect1Activated) + { + ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster(); + if (target != null) + { + transformDestList.Add(CardId.ExosisterGibrine); + return true; + } + } + } + + // become target + if ((DefaultOnBecomeTarget() && !Util.ChainContainsCard(_CardId.EvenlyMatched)) || (Duel.CurrentChain.Any(c => c == Card) && Duel.LastChainPlayer != 0)) + { + targetedMagnificaList.Add(Card); + transformDestList.AddRange(new List{CardId.ExosistersMagnifica, CardId.ExosisterMikailis, CardId.ExosisterGibrine, CardId.ExosisterKaspitell, CardId.ExosisterAsophiel}); + return true; + } + } + return false; + } + public bool ExosistersMagnificaActivateBanish() + { + // banish + if (ActivateDescription == Util.GetStringId(CardId.ExosistersMagnifica, 0)) + { + if (CheckWhetherNegated()) + { + return false; + } + // banish problem card + ClientCard target = GetProblematicEnemyCard(); + bool isProblemCard = false; + if (target != null) + { + isProblemCard = true; + Logger.DebugWriteLine("===Exosister: magnifica target 1: " + target?.Name); + } + + // banish target + if (Duel.LastChainPlayer == 1 && target == null) + { + List currentTargetList = Duel.LastChainTargets.Where(card => card.Controller == 1 && + (card.Location == CardLocation.MonsterZone || card.Location == CardLocation.SpellZone || card.Location == CardLocation.FieldZone)).ToList(); + if (currentTargetList.Count() > 0) + { + target = ShuffleCardList(currentTargetList)[0]; + Logger.DebugWriteLine("===Exosister: magnifica target 2: " + target?.Name); + } + } + + // dump banish + if (target == null) + { + target = GetBestEnemyCard(false, false); + bool check1 = !DefaultOnBecomeTarget() || Util.ChainContainsCard(_CardId.EvenlyMatched); + bool check2 = !targetedMagnificaList.Contains(Card); + bool check3 = !Bot.UnderAttack; + bool check4 = Duel.Phase != DuelPhase.End; + bool check5 = Duel.Player == 0 || Enemy.GetMonsterCount() < 2; + Logger.DebugWriteLine("===Exosister: magnifica check flag: " + check1 + " " + check2 + " " + check3 + " " + check4 + " " + check5); + if (check1 && check2 && check3 && check4 && check5) + { + target = null; + } + } + + if (target != null && (Duel.LastChainPlayer != 0 || Util.GetLastChainCard() == Card)) + { + if (isProblemCard) + { + removeChosenList.Add(target); + } + Logger.DebugWriteLine("===Exosister: magnifica target final: " + target?.Name); + activatedMagnificaList.Add(Card); + AI.SelectCard(CardId.ExosisterGibrine, CardId.ExosisterAsophiel, CardId.ExosisterKaspitell, CardId.ExosisterMikailis); + AI.SelectNextCard(target); + return true; + } + + return false; + } + return false; + } + + public bool ExosisterPaxActivate() + { + if (potActivate || Bot.LifePoints <= 800) + { + return false; + } + + List checkListForSpSummon = new List{ + CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterStella, CardId.ExosisterMartha, CardId.ExosisterElis + }; + List checkListForSearch = new List{ + CardId.ExosisterMartha, CardId.ExosisterStella, CardId.ExosisterVadis, CardId.ExosisterReturnia, CardId.ExosisterSophia, + CardId.ExosisterIrene, CardId.ExosisterArment, CardId.ExosisterElis + }; + if (Duel.Player == 0 && Duel.LastChainPlayer != 0) + { + // search returnia for banish + if (CheckAtAdvantage() && GetProblematicEnemyCard(true) != null && CheckRemainInDeck(CardId.ExosisterReturnia) > 0 && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + if (Bot.GetMonsterCount() > 0 && Bot.GetMonsters().All(card => card.IsFaceup() && card.HasSetcode(SetcodeExosister))) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterReturnia); + paxCallToField = false; + return true; + } + } + + // search martha for activate + if (CheckMarthaActivatable() && CheckRemainInDeck(CardId.ExosisterMartha) > 0 && !Bot.HasInHand(CardId.ExosisterMartha)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterMartha); + paxCallToField = false; + return true; + } + + // stella relative + if (!stellaEffect1Activated && CheckCalledbytheGrave(CardId.ExosisterStella) == 0) + { + // try to search stella + if (Bot.Hand.Count(card => card.IsCode(CardId.ExosisterStella)) == 0 && CheckRemainInDeck(CardId.ExosisterStella) > 0) + { + bool shouldSpSummon = !CheckLessOperation() && summoned && Bot.HasInMonstersZoneOrInGraveyard(CardId.ExosisterElis); + if (Bot.Hand.Any(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister))) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterStella); + paxCallToField = shouldSpSummon; + return true; + } + } + + // search monster for stella to summon + bool searchExosisterMonster = false; + if (Bot.HasInHand(CardId.ExosisterStella) && Bot.Hand.Count(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister)) == 1) + { + searchExosisterMonster = true; + } + if (Bot.HasInMonstersZone(CardId.ExosisterStella) && Bot.Hand.Count(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister)) == 0) + { + searchExosisterMonster = true; + } + if (searchExosisterMonster) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterMartha, CardId.ExosisterStella, CardId.ExosisterElis); + paxCallToField = false; + return true; + } + } + + // addition summon + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1 && summoned && !CheckLessOperation()) + { + if ( (sakitamaEffect1Activated || !Bot.HasInHand(CardId.Sakitama)) + && (stellaEffect1Activated || !Bot.HasInMonstersZone(CardId.ExosisterStella)) + && (elisEffect1Activated || !Bot.HasInHand(CardId.ExosisterElis)) + ) + { + foreach (int checkId in checkListForSpSummon) + { + int checkTarget = CheckExosisterMentionCard(checkId); + if (checkTarget > 0 && Bot.HasInMonstersZoneOrInGraveyard(checkId) && CheckRemainInDeck(checkTarget) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(checkId); + paxCallToField = true; + return true; + } + } + } + } + } + + // in danger + bool inDanger = CheckInDanger(); + + // trigger transform + CheckEnemyMoveGrave(); + + bool forReturnia = false; + if (!oncePerTurnEffectActivatedList.Contains(CardId.ExosisterReturnia) && Bot.HasInSpellZone(CardId.ExosisterReturnia) && Bot.GetMonsters().Count() == 0) + { + forReturnia = true; + } + + // become target + if (enemyMoveGrave || DefaultOnBecomeTarget() || inDanger || forReturnia) + { + List checkList = checkListForSpSummon; + bool shouldSpSummon = enemyMoveGrave || inDanger || forReturnia; + if (!shouldSpSummon && !Bot.HasInMonstersZone(new List{ + CardId.ExosisterElis, CardId.ExosisterStella, CardId.ExosisterIrene, CardId.ExosisterSophia, CardId.ExosisterMartha})) + { + shouldSpSummon = true; + } + if (CheckAtAdvantage() && !enemyMoveGrave) + { + shouldSpSummon = false; + checkList = checkListForSearch; + } + foreach (int checkId in checkList) + { + bool checkSuccessFlag = false; + + if (shouldSpSummon) + { + int checkTarget = CheckExosisterMentionCard(checkId); + checkSuccessFlag = checkTarget > 0 && Bot.HasInMonstersZoneOrInGraveyard(checkTarget) && CheckRemainInDeck(checkId) > 0 + && !exosisterTransformEffectList.Contains(checkId) && !Bot.HasInMonstersZone(checkId); + } else + { + checkSuccessFlag = !Bot.HasInHandOrHasInMonstersZone(checkId) && !Bot.HasInSpellZone(checkId) && CheckRemainInDeck(checkId) > 0; + } + + if (checkSuccessFlag) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(checkId); + paxCallToField = shouldSpSummon; + return true; + } + } + } + + return false; + } + + public bool ExosisterPaxActivateForEndSearch() + { + if (potActivate || Bot.LifePoints <= 800) + { + return false; + } + + if (Duel.Player == 0 || Duel.Phase >= DuelPhase.End) + { + // search spell/trap + List checkSpellTrapListForSearch = new List{ + CardId.ExosisterVadis, CardId.ExosisterMartha, CardId.ExosisterReturnia, CardId.ExosisterStella, CardId.ExosisterSophia, + CardId.ExosisterIrene, CardId.ExosisterArment, CardId.ExosisterElis + }; + foreach (int checkId in checkSpellTrapListForSearch) + { + if (!Bot.HasInHandOrHasInMonstersZone(checkId) && !Bot.HasInSpellZone(checkId) && CheckRemainInDeck(checkId) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterMartha, CardId.ExosisterStella, CardId.ExosisterElis); + paxCallToField = false; + return true; + } + } + } + return false; + } + + public bool ExosisterArmentActivate() + { + if (Bot.LifePoints <= 800) + { + return false; + } + ClientCard activateTarget = null; + + if (Duel.Player == 0) + { + bool decided = false; + + // addition summon + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1 && summoned && !CheckLessOperation()) + { + if ( (sakitamaEffect1Activated || !Bot.HasInHand(CardId.Sakitama)) + && (stellaEffect1Activated || !Bot.HasInMonstersZone(CardId.ExosisterStella)) + && (elisEffect1Activated || !Bot.HasInHand(CardId.ExosisterElis)) + ) + { + decided = true; + } + } + + if (Duel.LastChainPlayer == 1) + { + foreach (ClientCard target in Duel.LastChainTargets) + { + if (target.Controller == 0 && target.Location == CardLocation.MonsterZone && target.IsFaceup() && target.HasSetcode(SetcodeExosister)) + { + activateTarget = target; + decided = true; + break; + } + } + } + + if (!decided) + { + return false; + } + } + + if (activateTarget == null && Duel.LastChainPlayer == 1) + { + { + foreach (ClientCard target in Duel.LastChainTargets) + { + if (target.Controller == 0 && target.Location == CardLocation.MonsterZone && target.IsFaceup() && target.HasSetcode(SetcodeExosister)) + { + activateTarget = target; + break; + } + } + } + } + + if (activateTarget == null) + { + List targetList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasSetcode(SetcodeExosister) && !card.HasType(CardType.Xyz)).ToList(); + if (targetList.Count() > 0) + { + targetList.Sort(CardContainer.CompareCardAttack); + activateTarget = targetList[0]; + } + } + + if (activateTarget == null) + { + return false; + } + + // mikailis + if (!Bot.HasInMonstersZone(CardId.ExosisterMikailis) && !mikailisEffect1Activated && (Duel.Player == 1 || !mikailisEffect3Activated) + && !transformDestList.Contains(CardId.ExosisterMikailis) && Bot.HasInExtra(CardId.ExosisterMikailis)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterMikailis); + return true; + } + + // kaspitell on bot's turn + if (!Bot.HasInMonstersZone(CardId.ExosisterKaspitell) && !kaspitellEffect3Activated && Duel.Player == 0 + && !transformDestList.Contains(CardId.ExosisterKaspitell) && Bot.HasInExtra(CardId.ExosisterKaspitell)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterKaspitell); + return true; + } + + // gibrine + if (!Bot.HasInMonstersZone(CardId.ExosisterGibrine) && !gibrineEffect1Activated + && !transformDestList.Contains(CardId.ExosisterGibrine) && Bot.HasInExtra(CardId.ExosisterGibrine)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterGibrine); + return true; + } + + // asophiel + if (!Bot.HasInMonstersZone(CardId.ExosisterAsophiel) && !asophielEffect1Activated + && !transformDestList.Contains(CardId.ExosisterAsophiel) && Bot.HasInExtra(CardId.ExosisterAsophiel)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterAsophiel); + return true; + } + + // kaspitell on bot's turn + if (!Bot.HasInMonstersZone(CardId.ExosisterKaspitell) && !kaspitellEffect1Activated + && !transformDestList.Contains(CardId.ExosisterKaspitell) && Bot.HasInExtra(CardId.ExosisterKaspitell)) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(activateTarget); + transformDestList.Add(CardId.ExosisterKaspitell); + return true; + } + + return false; + } + + public bool ExosisterVadisActivate() + { + if (Bot.LifePoints <= 800) + { + return false; + } + + List checkListForSpSummon = new List{ + CardId.ExosisterSophia, CardId.ExosisterIrene, CardId.ExosisterStella, CardId.ExosisterMartha, CardId.ExosisterElis + }; + + bool decideToActivate = false; + bool checkTransform = false; + + // special summon for xyz + if (Duel.Player == 0 && Duel.Phase > DuelPhase.Draw && !CheckLessOperation()) + { + decideToActivate = true; + } + + // move grave + CheckEnemyMoveGrave(); + if (enemyMoveGrave) + { + decideToActivate = true; + checkTransform = true; + } + + // for returia + if (!oncePerTurnEffectActivatedList.Contains(CardId.ExosisterReturnia) && Bot.HasInSpellZone(CardId.ExosisterReturnia) && Bot.GetMonsters().Count() == 0) + { + decideToActivate = true; + } + + if (CheckInDanger() || (DefaultOnBecomeTarget() && !Util.ChainContainsCard(_CardId.EvenlyMatched))) + { + decideToActivate = true; + } + + if (decideToActivate) + { + foreach (int checkId in checkListForSpSummon) + { + int checkTarget = CheckExosisterMentionCard(checkId); + if (checkTarget > 0 && CheckRemainInDeck(checkId) > 0 && CheckRemainInDeck(checkTarget) > 0) + { + if (checkTransform) + { + int canTransformCount = 0; + foreach (int transformCheckId in new List{checkId, checkTarget}) + { + if (!Bot.HasInMonstersZone(checkId) && !exosisterTransformEffectList.Contains(checkId)) + { + canTransformCount ++; + } + } + + if (canTransformCount == 0) + { + continue; + } + } + oncePerTurnEffectActivatedList.Add(Card.Id); + Logger.DebugWriteLine("Exosiseter Vadis decide: " + checkId); + AI.SelectCard(checkId); + AI.SelectNextCard(checkTarget); + return true; + } + } + } + + return false; + } + + public bool ExosisterReturniaActivate() + { + if (Bot.LifePoints <= 800) + { + return false; + } + + // banish problem card + ClientCard target = GetProblematicEnemyCard(true); + if (target != null && Duel.LastChainPlayer != 0) + { + Logger.DebugWriteLine("===Exosister: returnia target 1: " + target?.Name); + removeChosenList.Add(target); + oncePerTurnEffectActivatedList.Add(Card.Id); + AI.SelectCard(target); + return true; + } + + // banish target + if (Duel.LastChainPlayer == 1) + { + List targetList = Duel.LastChainTargets.Where(card => card.Controller == 1 && + (card.Location == CardLocation.Grave || card.Location == CardLocation.MonsterZone || card.Location == CardLocation.SpellZone || card.Location == CardLocation.FieldZone)).ToList(); + if (targetList.Count() > 0) + { + oncePerTurnEffectActivatedList.Add(Card.Id); + List shuffleTargetList = ShuffleCardList(targetList); + Logger.DebugWriteLine("===Exosister: returnia target 2: " + shuffleTargetList[0]?.Name); + AI.SelectCard(shuffleTargetList); + return true; + } + } + + // dump banish + target = GetBestEnemyCard(false, true, true); + bool check1 = DefaultOnBecomeTarget() && target != null && (target.Location != CardLocation.Onfield || target.Id != _CardId.EvenlyMatched); + bool check2 = Bot.UnderAttack; + bool check3 = (Duel.Player == 1 && Duel.Phase == DuelPhase.End && Duel.LastChainPlayer != 0 && target != null && target.Location != CardLocation.Grave); + bool check4 = (Duel.Player == 1 && Enemy.GetMonsterCount() >= 2 && Duel.LastChainPlayer != 0); + Logger.DebugWriteLine("===Exosister: returnia check flag: " + check1 + " " + check2 + " " + check3 + " " + check4); + if (check1 || check2 || check3 || check4) + { + oncePerTurnEffectActivatedList.Add(Card.Id); + Logger.DebugWriteLine("===Exosister: returnia target 3: " + target?.Name); + AI.SelectCard(target); + return true; + } + + return false; + } + + /// + /// Check hand like exosister + elis + martha + /// + public bool ExosisterAvoidMaxxCSummonCheck() + { + if (!Bot.HasInHand(CardId.ExosisterMartha) || !Bot.HasInHand(CardId.ExosisterElis) || elisEffect1Activated || marthaEffect1Activated) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + // normal summon non-elis exosister + if (Card.Id != CardId.ExosisterElis && Card.Id != CardId.ExosisterMartha) + { + summoned = true; + return true; + } + // normal summon elis + if (Card.IsCode(CardId.ExosisterElis)) + { + int otherExosisterCount = Bot.Hand.Count(card => card?.Data != null && !card.IsCode(CardId.ExosisterElis) && !card.IsCode(CardId.ExosisterMartha) + && card.IsMonster() && card.HasSetcode(SetcodeExosister)); + if (otherExosisterCount > 0) + { + return false; + } + if (Bot.Hand.Count(card => card?.Data != null && card.IsCode(CardId.ExosisterElis)) > 1) + { + summoned = true; + return true; + } + } + + return false; + } + + /// + /// Check hand like exosister + stella + /// + public bool ExosisterStellaSummonCheck() + { + if (stellaEffect1Activated || Bot.HasInMonstersZone(CardId.ExosisterStella, true) || CheckWhetherNegated(true) || CheckLessOperation()) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + + int summonableCount = Bot.Hand.Count(card => card != Card && card?.Data != null && card.IsMonster() + && card.HasSetcode(SetcodeExosister)); + + if (summonableCount > 0) + { + summoned = true; + return true; + } + return false; + } + + /// + /// Check whether need Irene's redraw effect to search elis for xyz + /// + public bool ExosisterIreneSummonCheck() + { + if (irenaEffect1Activated || CheckLessOperation() + || CheckWhetherNegated(true) || CheckCalledbytheGrave(CardId.ExosisterElis) > 0 || CheckCalledbytheGrave(CardId.ExosisterIrene) > 0) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + + if (CheckRemainInDeck(CardId.ExosisterElis) > 0) + { + summoned = true; + return true; + } + return false; + } + + /// + /// Check hand like exosister + elis + /// + public bool ExosisterForElisSummonCheck() + { + if (elisEffect1Activated || CheckCalledbytheGrave(CardId.ExosisterElis) > 0 || CheckLessOperation()) + { + return false; + } + if (Card?.Data == null) + { + return false; + } + if (!Card.HasSetcode(SetcodeExosister) || (Card.IsCode(CardId.ExosisterMartha) && CheckRemainInDeck(CardId.ExosisterElis) > 0)) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + + if (Bot.Hand.Count(card => card != Card && card?.Data != null && card.IsCode(CardId.ExosisterElis)) > 0) + { + summoned = true; + return true; + } + + return false; + } + + public bool AratamaSummonCheck() + { + if (sakitamaEffect1Activated || CheckCalledbytheGrave(CardId.Aratama) > 0 || CheckCalledbytheGrave(CardId.Sakitama) > 0) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + if (CheckRemainInDeck(CardId.Sakitama) > 0) + { + summoned = true; + return true; + } + return false; + } + + public bool ForSakitamaSummonCheck() + { + if (sakitamaEffect1Activated || CheckCalledbytheGrave(CardId.Sakitama) > 0 || CheckLessOperation()) + { + return false; + } + if (Bot.Hand.Count(card => card?.Data != null && Card != card && card.IsCode(CardId.Sakitama)) == 0) + { + return false; + } + if (enemyActivateLockBird && CheckAtAdvantage()) + { + return false; + } + if (Card?.Data != null && !Card.IsCode(CardId.ExosisterMartha) && Card.Level == 4) + { + summoned = true; + return true; + } + + return false; + } + + public bool Level4SummonCheck() + { + if (Card.Id == CardId.ExosisterMartha) + { + return false; + } + if (Bot.GetMonsters().Count(card => CheckAbleForXyz(card)) == 1) + { + summoned = true; + return true; + } + return false; + } + + public bool ForDonnerSummonCheck() + { + if (!Bot.HasInExtra(CardId.DonnerDaggerFurHire) || (!Bot.HasInHand(CardId.ExosisterMartha) && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia))) + { + return false; + } + if (CheckLessOperation()) + { + return false; + } + + List illegalList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz) && card.Level != 4 + && (card.Data == null || !card.HasSetcode(SetcodeExosister))).ToList(); + if (illegalList.Count() == 0) + { + return false; + } + + if (illegalList.Count() == 1) + { + List otherMaterialList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz)).ToList(); + otherMaterialList.Sort(CardContainer.CompareCardAttack); + illegalList.AddRange(otherMaterialList); + } + if (illegalList.Count() == 1) + { + List hands = Bot.Hand.Where(card => card?.Data != null && card.IsMonster()).ToList(); + if (hands.Count() > 0) + { + hands.Sort(CardContainer.CompareCardAttack); + if (Card != hands[0]) + { + return false; + } + } + Logger.DebugWriteLine("===Exosister: summon for donner"); + summoned = true; + return true; + } + + return false; + } + + public bool ExosisterForArmentSummonCheck() + { + if (!Bot.HasInHandOrInSpellZone(CardId.ExosisterArment)) + { + return false; + } + if (Card?.Data == null) + { + return false; + } + if (!Card.HasSetcode(SetcodeExosister)) + { + return false; + } + + if (!Bot.GetMonsters().Any(card => card?.Data != null && card.IsFaceup() && card.HasSetcode(SetcodeExosister))) + { + summoned = true; + return true; + } + + return false; + } + + public bool ExosisterMikailisSpSummonCheck() + { + return ExosisterMikailisSpSummonCheckInner(true); + } + + public bool ExosisterMikailisAdvancedSpSummonCheck() + { + if (!CheckLessOperation() || enemyActivateLockBird) + { + return false; + } + + return ExosisterMikailisSpSummonCheckInner(false); + } + + public bool ExosisterMikailisSpSummonCheckInner(bool shouldCheckLessOperation = true) + { + if (Bot.HasInMonstersZone(CardId.ExosisterMikailis) || mikailisEffect3Activated || (CheckLessOperation() && shouldCheckLessOperation)) + { + return false; + } + + // check searched spell/trap + if (!enemyActivateLockBird) + { + foreach (int cardId in ExosisterSpellTrapList) + { + if (!Bot.HasInHandOrInSpellZone(cardId)) + { + SelectXyzMaterial(2); + return true; + } + } + } + + // clear enemy card + if (!mikailisEffect1Activated && !Bot.HasInMonstersZone(CardId.ExosisterMikailis)) + { + ClientCard target = GetProblematicEnemyCard(true); + if (target != null) + { + List exosisterMaterialList = Bot.GetMonsters().Where(card => CheckAbleForXyz(card) && card.HasSetcode(SetcodeExosister)).ToList(); + if (exosisterMaterialList?.Count() > 0) + { + SelectXyzMaterial(2, true); + return true; + } + } + } + + return false; + } + + public bool ExosisterKaspitellSpSummonCheck() + { + return ExosisterKaspitellSpSummonCheckInner(true); + } + + public bool ExosisterKaspitellAdvancedSpSummonCheck() + { + if (!CheckLessOperation() || enemyActivateLockBird) + { + return false; + } + return ExosisterKaspitellSpSummonCheckInner(false); + } + + public bool ExosisterKaspitellSpSummonCheckInner(bool shouldCheckLessOperation = true) + { + if (Bot.HasInMonstersZone(CardId.ExosisterKaspitell) || kaspitellEffect3Activated || (shouldCheckLessOperation && CheckLessOperation())) + { + return false; + } + + bool searchMartha = true; + bool searchStella = true; + bool forMagnifica = false; + if (marthaEffect1Activated || CheckCalledbytheGrave(CardId.ExosisterMartha) > 0 + || CheckRemainInDeck(CardId.ExosisterMartha) == 0 || CheckRemainInDeck(CardId.ExosisterElis) == 0) + { + searchMartha = false; + } + if (Bot.GetMonsters().Any(card => card.HasType(CardType.Link) || card.HasType(CardType.Token))) + { + searchMartha = false; + } + if (stellaEffect1Activated || CheckCalledbytheGrave(CardId.ExosisterStella) > 0 || CheckRemainInDeck(CardId.ExosisterStella) == 0 + || !Bot.Hand.Any(card => card?.Data != null && card.IsMonster() && card.HasSetcode(SetcodeExosister))) + { + searchStella = false; + } + if (Bot.GetMonsters().Count(card => card?.Data != null + && card.HasType(CardType.Xyz) && card.HasType(CardType.Xyz) && !card.IsCode(CardId.ExosistersMagnifica)) == 1) + { + forMagnifica = true; + } + if (enemyActivateLockBird) + { + searchMartha = false; + searchStella = false; + } + + if (!searchMartha && !searchStella && !forMagnifica) + { + return false; + } + + List materialCheckList = Bot.GetMonsters().Where(card => + !card.HasType(CardType.Xyz) && !card.HasType(CardType.Token) && !card.HasType(CardType.Link)).ToList(); + if (materialCheckList.Count() == 2 && materialCheckList.All(card => card.Level == 4)) + { + SelectXyzMaterial(2); + return true; + } + + return false; + } + + public bool ExosistersMagnificaSpSummonCheck() + { + if (CheckLessOperation()) + { + return false; + } + + List materialList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasType(CardType.Xyz) + && card.Rank == 4 && card.HasSetcode(SetcodeExosister)).ToList(); + materialList.Sort(CardContainer.CompareCardAttack); + + AI.SelectMaterials(materialList); + return true; + } + + public bool CheckCaduceusInner(ClientCard card) + { + if (card?.Data == null) + { + return false; + } + foreach (int setcode in SetcodeForDiamond) + { + if (card.HasSetcode(setcode)) + { + return true; + } + } + return false; + } + + public bool TellarknightConstellarCaduceusSpSummonCheck() + { + if (Duel.Turn == 1 || !Bot.HasInExtra(CardId.StellarknightConstellarDiamond)) + { + return false; + } + + // check whether need to call Diamond + if (Enemy.Graveyard.Any(card => CheckCaduceusInner(card))) + { + SelectXyzMaterial(2); + return true; + } + + return false; + } + + public bool DonnerDaggerFurHireSpSummonCheck() + { + if (!Bot.HasInHand(CardId.ExosisterMartha) && !Bot.HasInHandOrInSpellZone(CardId.ExosisterReturnia)) + { + return false; + } + + if (CheckLessOperation()) + { + return false; + } + + List illegalList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz) && card.Level != 4 + && (card.Data == null || !card.HasSetcode(SetcodeExosister))).ToList(); + + if (illegalList.Count() == 1) + { + + List otherMaterialList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz)).ToList(); + otherMaterialList.Sort(CardContainer.CompareCardAttack); + illegalList.AddRange(otherMaterialList); + } + if (illegalList.Count() > 1) + { + AI.SelectMaterials(illegalList); + return true; + } + + return false; + } + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + List onlyOneSetList = new List{ + CardId.ExosisterPax, CardId.ExosisterArment, CardId.ExosisterVadis, CardId.ExosisterReturnia + }; + if (onlyOneSetList.Contains(Card.Id) && Bot.HasInSpellZone(Card.Id)) + { + return false; + } + + // select place + if ((Card.IsTrap() || Card.HasType(CardType.QuickPlay))) + { + List avoid_list = new List(); + int setFornfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setFornfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setFornfiniteImpermanence); + return true; + } else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } else + { + SelectSTPlace(); + } + return true; + } + + return false; + } + + protected override bool DefaultSetForDiabellze() + { + if (base.DefaultSetForDiabellze()) + { + SelectSTPlace(null, true); + return true; + } + return false; + } + } +} diff --git a/Game/AI/Decks/FrogExecutor.cs b/Game/AI/Decks/FrogExecutor.cs index 84be2717..49667fa6 100644 --- a/Game/AI/Decks/FrogExecutor.cs +++ b/Game/AI/Decks/FrogExecutor.cs @@ -135,6 +135,7 @@ private bool FlipFlopFrog() private bool Ronintoadin() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; List monsters = Bot.GetGraveyardMonsters(); if (monsters.Count > 2) { diff --git a/Game/AI/Decks/GrenMajuThunderBoarderExecutor.cs b/Game/AI/Decks/GrenMajuThunderBoarderExecutor.cs index b256a695..c3be2755 100644 --- a/Game/AI/Decks/GrenMajuThunderBoarderExecutor.cs +++ b/Game/AI/Decks/GrenMajuThunderBoarderExecutor.cs @@ -1,4 +1,4 @@ -using YGOSharp.OCGWrapper.Enums; +using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using WindBot; using WindBot.Game; @@ -128,6 +128,7 @@ public override void OnNewTurn() { eater_eff = false; CardOfDemiseeff_used = false; + base.OnNewTurn(); } public override void OnNewPhase() @@ -437,6 +438,7 @@ private bool ThunderKingRaiOhsummon() private bool ThunderKingRaiOheff() { + if (DefaultOnlyHorusSpSummoning()) return false; if(Duel.SummoningCards.Count > 0) { foreach(ClientCard m in Duel.SummoningCards) @@ -570,6 +572,7 @@ private bool WakingTheDragoneff() private bool MetalSnakesp() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (ActivateDescription == Util.GetStringId(CardId.MetalSnake, 0) && !Bot.HasInMonstersZone(CardId.MetalSnake)) { if(Duel.Player == 1 && Duel.Phase >= DuelPhase.BattleStart ) diff --git a/Game/AI/Decks/HorusExecutor.cs b/Game/AI/Decks/HorusExecutor.cs index bc8b995f..de0daa38 100644 --- a/Game/AI/Decks/HorusExecutor.cs +++ b/Game/AI/Decks/HorusExecutor.cs @@ -153,7 +153,7 @@ private bool WhiteNightDragon() { // We should summon Horus the Black Flame Dragon LV6 if he can lvlup. if (Enemy.GetMonsterCount() != 0 && !Util.IsAllEnemyBetterThanValue(2300 - 1, false)) - foreach (ClientCard card in Main.SummonableCards) + foreach (ClientCard card in Duel.MainPhase.SummonableCards) if (card.IsCode(11224103)) return false; diff --git a/Game/AI/Decks/KashtiraExecutor.cs b/Game/AI/Decks/KashtiraExecutor.cs index 35f478f4..e271b61c 100644 --- a/Game/AI/Decks/KashtiraExecutor.cs +++ b/Game/AI/Decks/KashtiraExecutor.cs @@ -1,7 +1,9 @@ using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using System.Linq; - +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; namespace WindBot.Game.AI.Decks { [Deck("Kashtira", "AI_Kashtira")] @@ -173,6 +175,7 @@ public override void OnNewTurn() opt_2 = false; if (flag >= 0) ++flag; if (flag >= 2) { flag = -1; activate_DimensionShifter = false; } + base.OnNewTurn(); } public override bool OnSelectYesNo(long desc) { @@ -1372,4 +1375,4 @@ private bool KashtiraUnicornEffect() } } -} +} \ No newline at end of file diff --git a/Game/AI/Decks/LabrynthExecutor.cs b/Game/AI/Decks/LabrynthExecutor.cs new file mode 100644 index 00000000..f585d630 --- /dev/null +++ b/Game/AI/Decks/LabrynthExecutor.cs @@ -0,0 +1,4356 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using System; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Labrynth", "AI_Labrynth")] + public class LabrynthExecutor : DefaultExecutor + { + public class CardId + { + public const int LadyLabrynthOfTheSilverCastle = 81497285; + public const int LovelyLabrynthOfTheSilverCastle = 2347656; + public const int UnchainedSoulOfSharvara = 41165831; + public const int AriasTheLabrynthButler = 73602965; + public const int ArianeTheLabrynthServant = 75730490; + public const int AriannaTheLabrynthServant = 1225009; + public const int LabrynthChandraglier = 37629703; + // _CardId.AshBlossom = 14558127; + // _CardId.MaxxC = 23434538; + public const int LabrynthStovieTorbie = 74018812; + public const int LabrynthCooclock = 2511; + + public const int PotOfExtravagance = 49238328; + + public const int WelcomeLabrynth = 5380979; + public const int TransactionRollback = 6351147; + // _CardId.InfiniteImpermanence = 10045474; + public const int DestructiveDarumaKarmaCannon = 30748475; + public const int EscapeOfTheUnchained = 53417695; + // _CardId.DimensionalBarrier = 83326048; + public const int BigWelcomeLabrynth = 92714517; + + public const int ChaosAngel = 22850702; + public const int SuperStarslayerTYPHON = 93039339; + public const int UnchainedAbomination = 29479265; + public const int UnchainedSoulOfAnguish = 93084621; + public const int UnchainedSoulLordOfYama = 24269961; + public const int UnchainedSoulOfRage = 67680512; + public const int SPLittleKnight = 29301450; + public const int MuckrakerFromTheUnderworld = 71607202; + public const int RelinquishedAnima = 94259633; + + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + public const int SkillDrain = 82732705; + + public const int DimensionShifter = 91800273; + public const int MacroCosmos = 30241314; + public const int DimensionalFissure = 81674782; + public const int BanisheroftheRadiance = 94853057; + public const int BanisheroftheLight = 61528025; + public const int KashtiraAriseHeart = 48626373; + public const int AccesscodeTalker = 86066372; + public const int GhostMournerMoonlitChill = 52038441; + } + + public LabrynthExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + // startup effect/triggered chain + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + AddExecutor(ExecutorType.Activate, CardId.PotOfExtravagance, PotOfExtravaganceActivate); + AddExecutor(ExecutorType.Repos, CardId.LovelyLabrynthOfTheSilverCastle, ReposForLabrynth); + AddExecutor(ExecutorType.Activate, CardId.ChaosAngel, ChaosAngelActivate); + AddExecutor(ExecutorType.Activate, CardId.LovelyLabrynthOfTheSilverCastle, LovelyLabrynthOfTheSilverCastleActivate); + AddExecutor(ExecutorType.Activate, CardId.RelinquishedAnima, RelinquishedAnimaActivate); + AddExecutor(ExecutorType.Activate, CardId.AriannaTheLabrynthServant, AriannaTheLabrynthServantActivate); + AddExecutor(ExecutorType.Activate, CardId.ArianeTheLabrynthServant, ArianeTheLabrynthServantActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthChandraglier, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthStovieTorbie, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthCooclock, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.UnchainedSoulLordOfYama, UnchainedSoulLordOfYamaActivate); + AddExecutor(ExecutorType.Activate, CardId.WelcomeLabrynth, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.SuperStarslayerTYPHON, SuperStarslayerTYPHONActivate); + AddExecutor(ExecutorType.Activate, CardId.UnchainedAbomination, UnchainedAbominationActivate); + + // repos + AddExecutor(ExecutorType.Repos, CardId.ArianeTheLabrynthServant, ReposForLabrynth); + AddExecutor(ExecutorType.Repos, CardId.AriannaTheLabrynthServant, ReposForLabrynth); + + // negate/chain + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, CardId.LadyLabrynthOfTheSilverCastle, LadyLabrynthOfTheSilverCastleFieldActivate); + AddExecutor(ExecutorType.Activate, CardId.AriasTheLabrynthButler, RecycleActivate); + AddExecutor(ExecutorType.Activate, CardId.SPLittleKnight, SPLittleKnightActivate); + AddExecutor(ExecutorType.Activate, _CardId.DimensionalBarrier, DimensionalBarrierActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + + AddExecutor(ExecutorType.Activate, CardId.MuckrakerFromTheUnderworld, MuckrakerFromTheUnderworldActivate); + AddExecutor(ExecutorType.Activate, CardId.UnchainedSoulOfRage, UnchainedSoulOfRageActivate); + AddExecutor(ExecutorType.Activate, CardId.TransactionRollback, TransactionRollbackActivate); + AddExecutor(ExecutorType.Activate, CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonActivate); + AddExecutor(ExecutorType.Activate, CardId.EscapeOfTheUnchained, EscapeOfTheUnchainedActivate); + + // sp summon + AddExecutor(ExecutorType.Activate, CardId.LadyLabrynthOfTheSilverCastle, LadyLabrynthOfTheSilverCastleHandActivate); + AddExecutor(ExecutorType.Activate, CardId.BigWelcomeLabrynth, BigWelcomeLabrynthBecomeTargetActivate); + AddExecutor(ExecutorType.Activate, CardId.WelcomeLabrynth, WelcomeLabrynthActivate); + AddExecutor(ExecutorType.Activate, CardId.BigWelcomeLabrynth, BigWelcomeLabrynthActivate); + + // clock + AddExecutor(ExecutorType.Activate, CardId.AriasTheLabrynthButler, AriasTheLabrynthButlerActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthCooclock, LabrynthCooclockActivate); + AddExecutor(ExecutorType.Activate, CardId.BigWelcomeLabrynth, BigWelcomeLabrynthGraveActivate); + AddExecutor(ExecutorType.Activate, CardId.UnchainedSoulOfAnguish, UnchainedSoulOfAnguishActivate); + + // summon step + AddExecutor(ExecutorType.SpellSet, SpellSetForCooClockCheck); + AddExecutor(ExecutorType.Summon, CardId.ArianeTheLabrynthServant, ArianeTheLabrynthServantForRollbackSummon); + AddExecutor(ExecutorType.Summon, CardId.AriannaTheLabrynthServant, AriannaTheLabrynthServantSummon); + AddExecutor(ExecutorType.Summon, CardId.ArianeTheLabrynthServant, ArianeTheLabrynthServantSummon); + AddExecutor(ExecutorType.Summon, LabrynthForCooClockSummon); + AddExecutor(ExecutorType.Summon, ForLinkSummon); + AddExecutor(ExecutorType.Summon, ForSynchroSummon); + AddExecutor(ExecutorType.Summon, CardId.LabrynthCooclock, ForAnimaSummon); + + // furniture set + AddExecutor(ExecutorType.Activate, CardId.LabrynthChandraglier, FurnitureSetWelcomeActivate); + AddExecutor(ExecutorType.Activate, CardId.LabrynthStovieTorbie, FurnitureSetWelcomeActivate); + + // sp summon from extra + AddExecutor(ExecutorType.SpSummon, CardId.ChaosAngel, ChaosAngelSpSummonWith2Monster); + AddExecutor(ExecutorType.SpSummon, CardId.RelinquishedAnima, RelinquishedAnimaSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.UnchainedSoulLordOfYama, UnchainedSoulLordOfYamaSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.UnchainedSoulOfAnguish, UnchainedSoulOfAnguishSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.UnchainedSoulOfRage, UnchainedSoulOfRageSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.UnchainedAbomination, UnchainedAbominationSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SPLittleKnight, SPLittleKnightSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.ChaosAngel, ChaosAngelSpSummonWith3Monster); + AddExecutor(ExecutorType.SpSummon, CardId.MuckrakerFromTheUnderworld, MuckrakerFromTheUnderworldSpSummon); + + // hand eff + AddExecutor(ExecutorType.Activate, CardId.UnchainedSoulOfSharvara, UnchainedSoulOfSharvaraActivate); + + AddExecutor(ExecutorType.SpSummon, CardId.SuperStarslayerTYPHON, SuperStarslayerTYPHONSpSummon); + AddExecutor(ExecutorType.Repos, MonsterRepos); + AddExecutor(ExecutorType.Summon, SummonForTYPHONCheck); + AddExecutor(ExecutorType.SummonOrSet, ForBigWelcomeSummon); + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + } + + const int SetcodeTimeLord = 0x4a; + const int SetcodePhantom = 0xdb; + const int SetcodeOrcust = 0x11b; + const int SetcodeUnchained = 0x1130; + const int SetcodeLabrynth = 0x17f; + const int SetcodeHorus = 0x3; + const int hintTimingMainEnd = 0x4; + const int hintBattleStart = 0x8; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.AriannaTheLabrynthServant, CardId.LabrynthChandraglier, _CardId.AshBlossom, _CardId.MaxxC, + CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, _CardId.InfiniteImpermanence, CardId.BigWelcomeLabrynth }}, + {2, new List { CardId.LadyLabrynthOfTheSilverCastle, CardId.AriasTheLabrynthButler, CardId.PotOfExtravagance, CardId.WelcomeLabrynth, + CardId.TransactionRollback }}, + {1, new List { CardId.LovelyLabrynthOfTheSilverCastle, CardId.UnchainedSoulOfSharvara, CardId.ArianeTheLabrynthServant, + CardId.DestructiveDarumaKarmaCannon, CardId.EscapeOfTheUnchained, _CardId.DimensionalBarrier }} + }; + List notToNegateIdList = new List{ + 58699500, 20343502 + }; + List notToBeTrapTargetList = new List{ + 72144675, 86188410, 41589166, 11443677, 72566043, 1688285, 59071624, 6511113, 48183890, 952523, 22423493, 73639099 + }; + List targetNegateIdList = new List { + _CardId.EffectVeiler, _CardId.InfiniteImpermanence, CardId.GhostMournerMoonlitChill, _CardId.BreakthroughSkill, 74003290, 67037924, + 9753964, 66192538, 23204029, 73445448, 35103106, 30286474, 45002991, 5795980, 38511382, 53742162, 30430448 + }; + List notToDestroySpellTrap = new List { 50005218, 6767771 }; + + bool enemyActivateMaxxC = false; + List infiniteImpermanenceList = new List(); + bool summoned = false; + List activatedCardIdList = new List(); + List currentNegateMonsterList = new List(); + List currentDestroyCardList = new List(); + List setTrapThisTurn = new List(); + List summonThisTurn = new List(); + List enemySetThisTurn = new List(); + List escapeTargetList = new List(); + List summonInChainList = new List(); + bool cooclockAffected = false; + bool cooclockActivating = false; + bool furnitureActivating = false; + bool dimensionBarrierAnnouncing = false; + int banSpSummonExceptFiendCount = 0; + int dimensionShifterCount = 0; + int enemySpSummonFromExLastTurn = 0; + int enemySpSummonFromExThisTurn = 0; + bool enemyActivateInfiniteImpermanenceFromHand = false; + int rollbackCopyCardId = 0; + List dimensionalBarrierAnnouced = new List(); + List chainSummoningIdList = new List(3); + ClientCard bigwelcomeEscaseTarget = null; + + /// + /// Shuffle List and return a random-order card list + /// + public List ShuffleList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + T tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + List floodagateList = Enemy.GetMonsters().Where(c => c?.Data != null && + c.IsFloodgate() && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count() > 0) return floodagateList[0]; + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count() > 0) return dangerList[0]; + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count() > 0) return invincibleList[0]; + + List equippedList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.EquipCards.Count() > 0 && CheckCanBeTargeted(c, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(c))).OrderByDescending(card => card.Attack).ToList(); + if (equippedList.Count() > 0) return equippedList[0]; + + List enemyMonsters = Enemy.GetMonsters().OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count() > 0) + { + foreach (ClientCard target in enemyMonsters) + { + if ((target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && CheckCanBeTargeted(target, canBeTarget, selfType) && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(target)) + ) return target; + } + } + + if (attack >= 0) + { + if (attack == 0) + attack = Util.GetBestAttack(Bot); + List betterList = Enemy.MonsterZone.GetMonsters() + .Where(card => card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && CheckCanBeTargeted(card, canBeTarget, selfType) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).OrderByDescending(card => card.Attack).ToList(); + if (betterList.Count() > 0) return betterList[0]; + } + return null; + } + + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreSpells = false, CardType selfType = 0) + { + List resultList = new List(); + + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count() > 0) resultList.AddRange(floodagateList); + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).ToList(); + if (problemEnemySpellList.Count() > 0) resultList.AddRange(ShuffleList(problemEnemySpellList)); + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count() > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) resultList.AddRange(dangerList); + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count() > 0) resultList.AddRange(invincibleList); + + List enemyMonsters = Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c)).OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count() > 0) + { + foreach(ClientCard target in enemyMonsters) + { + if ( (target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && !resultList.Contains(target) && CheckCanBeTargeted(target, canBeTarget, selfType) + ) + { + resultList.Add(target); + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !currentDestroyCardList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) && CheckCanBeTargeted(c, canBeTarget, selfType) + && !notToDestroySpellTrap.Contains(c.Id)).ToList(); + if (spells.Count() > 0 && !ignoreSpells) resultList.AddRange(ShuffleList(spells)); + + return resultList; + } + + public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + ClientCard card = GetProblematicEnemyMonster(0, canBeTarget, ignoreCurrentDestroy, selfType); + if (card != null) + return card; + + card = Enemy.MonsterZone.Where(c => c?.Data != null && c.HasType(CardType.Monster) && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) && (!ignoreCurrentDestroy || currentDestroyCardList.Contains(c))) + .OrderByDescending(c => c.Attack).FirstOrDefault(); + if (card != null) + return card; + + List monsters = Enemy.GetMonsters().Where(c => !ignoreCurrentDestroy || currentDestroyCardList.Contains(c)).ToList(); + + // after GetHighestAttackMonster, the left monsters must be face-down. + if (monsters.Count() > 0 && !onlyFaceup) + return ShuffleList(monsters)[0]; + + return null; + } + + /// + /// check enemy's dangerous card in grave + /// + public List GetDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantom) || card.HasSetcode(SetcodeHorus))).ToList(); + List dangerMonsterIdList = new List{ + 99937011, 63542003, 9411399, 28954097, 30680659 + }; + result.AddRange(Enemy.Graveyard.GetMatchingCards(card => dangerMonsterIdList.Contains(card.Id))); + return result; + } + + public int GetEmptyMainMonsterZoneCount() + { + int remainCount = 0; + for (int idx = 0; idx < 5; ++idx) + { + if (Bot.MonsterZone[idx] == null) remainCount++; + } + return remainCount; + } + + public List GetNormalEnemyTargetList(bool canBeTarget = true, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + List targetList = GetProblematicEnemyCardList(canBeTarget, selfType: selfType); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && enemySetThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && !enemySetThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList())); + + return targetList; + } + + public List GetMonsterListForTargetNegate(bool canBeTarget = false, CardType selfType = 0) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; + } + + // negate before used + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && CheckCanBeTargeted(card, canBeTarget, selfType) + && !currentNegateMonsterList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + // negate monster effect on the field + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && CheckCanBeTargeted(chainingCard, canBeTarget, selfType) && !currentNegateMonsterList.Contains(chainingCard)) + { + resultList.Add(chainingCard); + } + } + + return resultList; + } + + public int GetMaterialAttack(List materials) + { + if (Util.IsTurn1OrMain2()) return 0; + int result = 0; + foreach (ClientCard material in materials) + { + if (material.IsAttack() || !summonThisTurn.Contains(material)) result += material.Attack; + } + return result; + } + + public int GetBotCurrentTotalAttack(List exceptList = null) + { + if (Util.IsTurn1OrMain2()) return 0; + int result = 0; + foreach (ClientCard monster in Bot.GetMonsters()) + { + if (exceptList != null && exceptList.Contains(monster)) continue; + if (monster.IsAttack() || !summonThisTurn.Contains(monster)) result += monster.Attack; + } + return result; + } + + public List GetCanBeUsedForLinkMaterial(bool useAdvancedMonster = false, Func exceptRule = null) + { + List materialList = Bot.GetMonsters().Where(card => { + if (card.IsFacedown() || (exceptRule != null && exceptRule(card))) return false; + if (card.IsCode(CardId.MuckrakerFromTheUnderworld) && summonThisTurn.Contains(card)) return false; + if (card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle) && !card.IsDisabled() && Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth)) return false; + if ((card.IsCode(CardId.ChaosAngel) || card.IsCode(CardId.LadyLabrynthOfTheSilverCastle)) + && !useAdvancedMonster && (card.IsAttack() || !summonThisTurn.Contains(card))) return false; + + return true; + }).ToList(); + materialList.Sort(CompareUsableAttack); + return materialList; + } + + public bool CheckCanDirectAttack() + { + return Enemy.GetMonsterCount() == 0 && !activatedCardIdList.Contains(CardId.SPLittleKnight) && Duel.Turn > 1 && Duel.Player == 0 && Duel.Phase < DuelPhase.Main2; + } + + /// + /// Check negated turn count of id + /// + public int CheckCalledbytheGrave(int id) + { + if (DefaultCheckWhetherCardIdIsNegated(id)) return 1; + return 0; + } + + public bool CheckCanBeTargeted(ClientCard card, bool canBeTarget, CardType selfType) + { + if (card == null) return true; + if (canBeTarget) + { + if (card.IsShouldNotBeTarget()) return false; + if (((int)selfType & (int)CardType.Monster) > 0 && card.IsShouldNotBeMonsterTarget()) return false; + if (((int)selfType & (int)CardType.Spell) > 0 && card.IsShouldNotBeSpellTrapTarget()) return false; + if (((int)selfType & (int)CardType.Trap) > 0 + && (card.IsShouldNotBeSpellTrapTarget() || (!card.IsDisabled() && notToBeTrapTargetList.Contains(card.Id)))) return false; + } + return true; + } + + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + public int CheckRemainInDeck(params int[] ids) + { + int sumResult = 0; + foreach (int id in ids) + { + sumResult += CheckRemainInDeck(id); + } + + return sumResult; + } + + /// + /// Whether spell or trap will be negate. If so, return true. + /// + /// is counter trap + /// check target + /// + public bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + // target default set + if (target == null) target = Card; + // won't negate if not on field + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + // negate judge + if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap() && (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true))) return true; + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) return true; + } + // how to get here? + return false; + } + + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated(bool disablecheck = true, bool toFieldCheck = false, CardType type = 0) + { + if ((Card.IsSpell() || Card.IsTrap() || (((int)type & (int)CardType.Spell) == 0) || (((int)type & (int)CardType.Trap) == 0)) && CheckSpellWillBeNegate()) + return true; + if (CheckCalledbytheGrave(Card.Id) > 0) return true; + if ((Card.IsMonster() || (((int)type & (int)CardType.Monster) == 0)) && (toFieldCheck || Card.Location == CardLocation.MonsterZone)) + { + if ((toFieldCheck && (((int)type & (int)CardType.Link) == 0)) || Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card)) || Bot.MonsterZone.Any(card => CheckNumber41(card))) return true; + } + if (Enemy.HasInSpellZone(CardId.SkillDrain, true, true)) return true; + } + if (disablecheck) return Card.IsDisabled(); + return false; + } + + public bool CheckNumber41(ClientCard card) + { + return card != null && card.IsFaceup() && card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled(); + } + + /// + /// Check whether cards will be removed. If so, do not send cards to grave. + /// + public bool CheckWhetherWillbeRemoved() + { + if (dimensionShifterCount > 0) return true; + List checkIdList = new List { CardId.BanisheroftheRadiance, CardId.BanisheroftheLight, CardId.MacroCosmos, CardId.DimensionalFissure, + CardId.KashtiraAriseHeart, 58481572 }; + foreach (int cardid in checkIdList) + { + List fields = new List { Bot, Enemy }; + foreach (ClientField cf in fields) + { + if (cf.HasInMonstersZone(cardid, true, false, true) || cf.HasInSpellZone(cardid, true, true)) + { + return true; + } + } + } + return false; + } + + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && (Duel.Player == 0 || Bot.GetMonsterCount() > 0)) return true; + return false; + } + + public bool CheckShouldNoMoreSpSummon(bool isLabrynth = true) + { + if (CheckAtAdvantage() && enemyActivateMaxxC && (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2)) + { + if (!isLabrynth) return true; + if (cooclockAffected) + { + if (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth))) return true; + if (Duel.Player == 0 && !summoned) return true; + if (setTrapThisTurn.Count() == 0) return true; + return false; + } + return true; + } + return false; + } + + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (notToNegateIdList.Contains(lastcard.Id)) return false; + + return true; + } + + public bool CheckChainContainEnemyMaxxC() + { + foreach (ClientCard card in Duel.CurrentChain) + { + if (card.Controller == 1 && card.IsCode(_CardId.MaxxC)) return true; + } + + return false; + } + + public bool CheckBigWelcomeCanSpSummon(int cardId) + { + return Bot.HasInHandOrInGraveyard(cardId) || CheckRemainInDeck(cardId) > 0; + } + + public int CompareUsableAttack(ClientCard cardA, ClientCard cardB) + { + if (cardA == null && cardB == null) + return 0; + if (cardA == null) + return -1; + if (cardB == null) + return 1; + int powerA = (cardA.IsDefense() && summonThisTurn.Contains(cardA)) ? 0 : cardA.Attack; + int powerB = (cardB.IsDefense() && summonThisTurn.Contains(cardB)) ? 0 : cardB.Attack; + if (powerA < powerB) + return -1; + if (powerA == powerB) + return CardContainer.CompareCardLevel(cardA, cardB); + return 1; + } + + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (currentSolvingChain != null) + { + if (currentSolvingChain.Controller == 1 && currentSolvingChain.IsCode(_CardId.EvenlyMatched)) + { + Logger.DebugWriteLine("=== Evenly Matched activated."); + List banishList = new List(); + List botMonsters = Bot.GetMonsters().Where(card => !card.HasType(CardType.Token)).ToList(); + + // monster + List faceDownMonsters = botMonsters.Where(card => card.IsFacedown()).ToList(); + banishList.AddRange(faceDownMonsters); + List notImportantMonster = botMonsters.Where(card => !banishList.Contains(card) + && ((card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && Bot.HasInExtra(card.Id)) + || CheckRemainInDeck(card.Id) > 0)).ToList(); + notImportantMonster.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(notImportantMonster); + + // spells + List faceUpSpells = Bot.GetSpells().Where(c => c.IsFaceup()).ToList(); + banishList.AddRange(ShuffleList(faceUpSpells)); + List faceDownSpells = Bot.GetSpells().Where(c => c.IsFacedown()).ToList(); + banishList.AddRange(ShuffleList(faceDownSpells)); + + List importantMonster = botMonsters.Where(card => !banishList.Contains(card) && !card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle) + && ((card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && !Bot.HasInExtra(card.Id)) + || CheckRemainInDeck(card.Id) == 0)).ToList(); + importantMonster.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(importantMonster); + + // lovely + List lovelyList = botMonsters.Where(card => !banishList.Contains(card) && card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)).ToList(); + lovelyList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(lovelyList); + + return Util.CheckSelectCount(banishList, cards, min, max); + } + + if (currentSolvingChain.IsCode(CardId.LadyLabrynthOfTheSilverCastle) && min == 1 && max == 1 && hint == HintMsg.Set) + { + SortedDictionary> trapCheckDict = new SortedDictionary>{ + {_CardId.DimensionalBarrier, DimensionalBarrierActivate}, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonSetCheck}, + {_CardId.InfiniteImpermanence, InfiniteImpermanenceSetCheck} + }; + foreach (KeyValuePair> pair in trapCheckDict) + { + ClientCard target = cards.FirstOrDefault(card => card.IsCode(pair.Key)); + if (target != null && pair.Value()) + { + SelectSTPlace(null, true); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + + ClientCard rollback = cards.FirstOrDefault(card => card.IsCode(CardId.TransactionRollback)); + if (rollback != null) + { + bool haveUnchainSoul = false; + if (!activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara)) + { + haveUnchainSoul |= Bot.HasInHand(CardId.UnchainedSoulOfSharvara); + haveUnchainSoul |= Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 + && Bot.HasInExtra(CardId.UnchainedSoulLordOfYama) && !activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama) + && (CheckRemainInDeck(CardId.UnchainedSoulOfSharvara) > 0 || Bot.HasInGraveyard(CardId.UnchainedSoulOfSharvara)) + && Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasRace(CardRace.Fiend) && card.Level <= 4).Count() >= 2; + } + bool haveAriane = false; + if (!activatedCardIdList.Contains(CardId.ArianeTheLabrynthServant) && Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2) + { + haveAriane |= Bot.HasInMonstersZone(CardId.ArianeTheLabrynthServant); + haveAriane |= Bot.HasInHand(CardId.ArianeTheLabrynthServant) && !summoned; + haveAriane |= Bot.GetSpells().Any(card => card.IsFacedown() && + ( + (card.IsCode(CardId.WelcomeLabrynth) && !activatedCardIdList.Contains(CardId.WelcomeLabrynth) && (cooclockAffected || !setTrapThisTurn.Contains(card))) + || + (card.IsCode(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && (cooclockAffected || !setTrapThisTurn.Contains(card))) + ) + ); + } + + if (haveUnchainSoul || haveAriane) + { + return Util.CheckSelectCount(new List { rollback }, cards, min, max); + } + } + + // welcome check + SortedDictionary welcomeCheck = new SortedDictionary { + {CardId.BigWelcomeLabrynth, cards.FirstOrDefault(card => card.IsCode(CardId.BigWelcomeLabrynth))}, + {CardId.WelcomeLabrynth, cards.FirstOrDefault(card => card.IsCode(CardId.WelcomeLabrynth))} + }; + List welcomeCheckIdList = new List { CardId.BigWelcomeLabrynth, CardId.WelcomeLabrynth }; + foreach (KeyValuePair checkPair in welcomeCheck) + { + if (checkPair.Value != null && !Bot.HasInHand(checkPair.Key) && !Bot.HasInGraveyard(checkPair.Key) + && !Bot.GetSpells().Any(card => card.IsCode(checkPair.Key) && card.IsFacedown())) + { + SelectSTPlace(null, true); + return Util.CheckSelectCount(new List { checkPair.Value }, cards, min, max); + } + } + if (welcomeCheck[CardId.BigWelcomeLabrynth] != null && + !Bot.HasInHand(CardId.BigWelcomeLabrynth) && !Bot.GetSpells().Any(card => card.IsCode(CardId.BigWelcomeLabrynth) && card.IsFacedown())) + { + SelectSTPlace(null, true); + return Util.CheckSelectCount(new List { welcomeCheck[CardId.BigWelcomeLabrynth] }, cards, min, max); + } + + // normal set + List checkIdList = new List{_CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon, + CardId.BigWelcomeLabrynth, CardId.TransactionRollback, CardId.WelcomeLabrynth}; + foreach (int checkId in checkIdList) + { + ClientCard checkCard = cards.FirstOrDefault(card => card.IsCode(checkId)); + if (checkCard != null) + { + SelectSTPlace(null, true); + return Util.CheckSelectCount(new List { checkCard }, cards, min, max); + } + } + } + + if (currentSolvingChain.IsCode(CardId.WelcomeLabrynth)) + { + banSpSummonExceptFiendCount = 2; + } + + if (currentSolvingChain.IsCode(CardId.WelcomeLabrynth) || (currentSolvingChain.IsCode(CardId.TransactionRollback) && rollbackCopyCardId == CardId.WelcomeLabrynth)) + { + Logger.DebugWriteLine("rewrite welcome's select."); + List selection = new List(); + + ClientCard ariane = GetWelcomeOrBigWelcomeTarget(cards, CardId.ArianeTheLabrynthServant); + if (ariane != null && !summonInChainList.Any(card => card.IsCode(CardId.ArianeTheLabrynthServant))) + { + if ((Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 || Duel.Player == 1 && Duel.Phase >= DuelPhase.Main2) + && Bot.HasInHandOrInSpellZone(CardId.TransactionRollback)) + { + selection.Add(ariane); + } + } + + ClientCard arianna = GetWelcomeOrBigWelcomeTarget(cards, CardId.AriannaTheLabrynthServant); + if (arianna != null && !summonInChainList.Any(card => card.IsCode(CardId.AriannaTheLabrynthServant))) + { + bool canActivateCheck = !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !CheckWhetherNegated(true, true, CardType.Monster); + if (canActivateCheck) + { + bool checkFlag = !(!activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && + (Bot.HasInGraveyard(CardId.BigWelcomeLabrynth) || Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth)))); + checkFlag |= !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && (CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle) || Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true)) + && Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth) + && (!setTrapThisTurn.Contains(card) || cooclockAffected)); + checkFlag |= !(Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) + || CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle)); + if (checkFlag) + { + selection.Add(arianna); + } + } + } + + ClientCard arias = GetWelcomeOrBigWelcomeTarget(cards, CardId.AriasTheLabrynthButler); + if (arias != null && !summonInChainList.Any(card => card.IsCode(CardId.AriasTheLabrynthButler)) && !Bot.HasInHandOrHasInMonstersZone(CardId.AriasTheLabrynthButler)) + { + bool canActivateCheck = !activatedCardIdList.Contains(CardId.AriasTheLabrynthButler) && !CheckWhetherNegated(true, true, CardType.Monster); + if (canActivateCheck && Bot.HasInHand(CardId.LovelyLabrynthOfTheSilverCastle)) + { + selection.Add(arias); + } + } + + ClientCard lovely = GetWelcomeOrBigWelcomeTarget(cards, CardId.LovelyLabrynthOfTheSilverCastle); + if (lovely != null && !summonInChainList.Any(card => card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle))) + { + if (Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)) + { + selection.Add(lovely); + } + } + + ClientCard lady = GetWelcomeOrBigWelcomeTarget(cards, CardId.LadyLabrynthOfTheSilverCastle); + if (lady != null) + { + if (Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasRace(CardRace.Fiend) && card.Level >= 8 && !card.HasType(CardType.Xyz | CardType.Link))) + { + selection.Add(lady); + } + } + + bool attackFlag = CheckCanDirectAttack(); + bool defenseFlag = Bot.UnderAttack && Bot.GetMonsterCount() == 0; + if (attackFlag || defenseFlag) + { + ClientCard bestPowerMonster = null; + int bestPower = -1; + foreach (ClientCard target in cards) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(target.Id); + if (cardData != null) + { + int power = attackFlag ? cardData.Attack : Math.Max(cardData.Attack, cardData.Defense); + if (bestPowerMonster == null || power > bestPower) + { + bestPowerMonster = target; + bestPower = power; + } + } + } + if (defenseFlag || GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack() + bestPower >= Enemy.LifePoints) + { + ClientCard realTarget = GetWelcomeOrBigWelcomeTarget(cards, bestPowerMonster.Id); + if (realTarget != null) selection.Add(realTarget); + } + } + + List checkIdList = new List{CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock, CardId.AriasTheLabrynthButler}; + foreach (int checkId in checkIdList) + { + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId)) + { + ClientCard target = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (target != null) selection.Add(target); + } + } + + List fullCheckIdList = new List{ + CardId.LadyLabrynthOfTheSilverCastle, CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock, + CardId.AriasTheLabrynthButler, CardId.ArianeTheLabrynthServant, CardId.AriannaTheLabrynthServant + }; + foreach (int checkId in fullCheckIdList) + { + ClientCard target = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (target != null && !selection.Contains(target)) selection.Add(target); + } + + if (selection.Count() > 0) return Util.CheckSelectCount(selection, cards, min, max); + } + + bool searchFlag = currentSolvingChain.IsCode(CardId.AriannaTheLabrynthServant) && hint == HintMsg.AddToHand; + bool bigwelcomeSoving = currentSolvingChain.IsCode(CardId.BigWelcomeLabrynth) || (currentSolvingChain.IsCode(CardId.TransactionRollback) && rollbackCopyCardId == CardId.BigWelcomeLabrynth); + searchFlag |= bigwelcomeSoving && hint == HintMsg.SpSummon && Bot.GetMonsterCount() == 0; + if (searchFlag) + { + Logger.DebugWriteLine("rewrite search."); + List selection = new List(); + + List furnitureCheckIdList = new List { CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.LabrynthChandraglier }; + ClientCard bigWelcome = GetWelcomeOrBigWelcomeTarget(cards, CardId.BigWelcomeLabrynth); + ClientCard welcome = GetWelcomeOrBigWelcomeTarget(cards, CardId.WelcomeLabrynth); + ClientCard arianna = GetWelcomeOrBigWelcomeTarget(cards, CardId.AriannaTheLabrynthServant); + + // search big welcome to activate this turn + if (Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2) + { + if (!summoned && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !CheckWhetherNegated(true, true, CardType.Monster) + && CheckCalledbytheGrave(CardId.AriannaTheLabrynthServant) == 0 && arianna != null && !Bot.HasInHand(CardId.AriannaTheLabrynthServant)) + { + return Util.CheckSelectCount(new List { arianna }, cards, min, max); + } + if (!CheckShouldNoMoreSpSummon()) + { + if (bigWelcome != null && !activatedCardIdList.Contains(CardId.AriasTheLabrynthButler) + && Bot.HasInHandOrHasInMonstersZone(CardId.AriasTheLabrynthButler)) + { + return Util.CheckSelectCount(new List { bigWelcome }, cards, min, max); + } + + bool canActivateSetTrap = (cooclockAffected || Bot.HasInHand(CardId.LabrynthCooclock)) + && Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)); + if (canActivateSetTrap && !Bot.HasInHandOrInSpellZone(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, CardId.BigWelcomeLabrynth) }, cards, min, max); + } + } + } + + // search cooclock/bulter to activate trap + ClientCard arias = null; + ClientCard cooclock = null; + if (!activatedCardIdList.Contains(CardId.AriasTheLabrynthButler) && CheckRemainInDeck(CardId.AriasTheLabrynthButler) > 0 + && !Bot.HasInHand(CardId.AriasTheLabrynthButler)) + { + arias = GetWelcomeOrBigWelcomeTarget(cards, CardId.AriasTheLabrynthButler); + } + if (!activatedCardIdList.Contains(CardId.LabrynthCooclock) && CheckRemainInDeck(CardId.LabrynthCooclock) > 0 + && !Bot.HasInHand(CardId.LabrynthCooclock)) + { + cooclock = GetWelcomeOrBigWelcomeTarget(cards, CardId.LabrynthCooclock); + } + if (arias != null || cooclock != null) + { + SortedDictionary> trapCheckDict = new SortedDictionary> { + {CardId.BigWelcomeLabrynth, BigWelcomeLabrynthSetCheck}, + {_CardId.DimensionalBarrier, DimensionalBarrierActivate}, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonSetCheck}, + {CardId.WelcomeLabrynth, WelcomeLabrynthSetCheck} + }; + foreach (KeyValuePair> checkPair in trapCheckDict) + { + if (Bot.GetSpells().Any(card => card.IsFacedown() && !setTrapThisTurn.Contains(card) && card.IsCode(checkPair.Key))) continue; + if (!activatedCardIdList.Contains(checkPair.Key)) + { + if (Bot.GetSpells().Any(card => card.IsFacedown() && setTrapThisTurn.Contains(card) && card.IsCode(checkPair.Key)) + && cooclock != null) + { + return Util.CheckSelectCount(new List { cooclock }, cards, min, max); + } + else if (Bot.HasInHand(checkPair.Key)) + { + if (checkPair.Value()) + { + if (arias != null) + { + return Util.CheckSelectCount(new List { arias }, cards, min, max); + } + if (Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 && cooclock != null) + { + return Util.CheckSelectCount(new List { cooclock }, cards, min, max); + } + } + } + } + } + } + + bool lackUnimportantCost = !Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth)); + if (lackUnimportantCost) + { + List handCost = Bot.Hand.Where(card => card != Card).ToList(); + lackUnimportantCost &= handCost.Count() <= 2 && !handCost.Any(card => !card.IsCode(_CardId.MaxxC, _CardId.AshBlossom, CardId.LabrynthCooclock)); + } + if (!lackUnimportantCost && cooclock != null && bigWelcome != null) + { + foreach (int furnitureId in furnitureCheckIdList) + { + if (furnitureId == CardId.LabrynthCooclock) continue; + if (CheckCalledbytheGrave(furnitureId) == 0 && !activatedCardIdList.Contains(furnitureId) && Bot.HasInHand(furnitureId)) + { + return Util.CheckSelectCount(new List { cooclock }, cards, min, max); + } + } + } + + if (Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2) + { + // search not exist furniture + if (!lackUnimportantCost) + { + foreach (int checkId in furnitureCheckIdList) + { + ClientCard furniture = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && furniture != null) + { + if (checkId == CardId.LabrynthCooclock) + { + if (Enemy.GetMonsterCount() > 0 && !Bot.HasInHandOrInMonstersZoneOrInGraveyard(CardId.LabrynthChandraglier)) continue; + } + return Util.CheckSelectCount(new List { furniture }, cards, min, max); + } + } + } + + // search big welcome + if (bigWelcome != null) + { + bool needSpSummonLovely = !Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) + && !Bot.HasInHandOrInSpellZone(CardId.BigWelcomeLabrynth) && cards.Any(c => c.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)); + needSpSummonLovely |= Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) && !Bot.HasInHandOrInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth); + if (needSpSummonLovely) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, CardId.BigWelcomeLabrynth) }, cards, min, max); + } + } + // search welcome + if (welcome != null && Bot.HasInHandOrInSpellZone(CardId.BigWelcomeLabrynth) && !Bot.HasInHandOrInSpellZone(CardId.WelcomeLabrynth)) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, CardId.WelcomeLabrynth) }, cards, min, max); + } + } + + // search big welcome/arias + if (Duel.Player == 1 && (Duel.Phase <= DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) + && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.AriasTheLabrynthButler) + && !Bot.HasInSpellZone(CardId.BigWelcomeLabrynth)) + { + if (Bot.HasInHand(CardId.BigWelcomeLabrynth) && !Bot.HasInHandOrHasInMonstersZone(CardId.AriasTheLabrynthButler) && arias != null) + { + return Util.CheckSelectCount(new List { arias }, cards, min, max); + } + if (Bot.HasInHand(CardId.AriasTheLabrynthButler) && !Bot.HasInHandOrHasInMonstersZone(CardId.BigWelcomeLabrynth) && bigWelcome != null) + { + return Util.CheckSelectCount(new List { bigWelcome }, cards, min, max); + } + } + + // search lady + ClientCard lady = GetWelcomeOrBigWelcomeTarget(cards, CardId.LadyLabrynthOfTheSilverCastle); + bool haveTrap = Duel.Player == 0 && Bot.Hand.Any(card => card.Type == (int)CardType.Trap) && Duel.Phase <= DuelPhase.Main2; + haveTrap |= Bot.GetSpells().Any(card => card.IsFacedown() && card.Type == (int)CardType.Trap); + if (!Bot.HasInHandOrHasInMonstersZone(CardId.LadyLabrynthOfTheSilverCastle) && !activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle) + && haveTrap && lady != null) + { + return Util.CheckSelectCount(new List { lady }, cards, min, max); + } + + if (!activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !CheckWhetherNegated(true, true, CardType.Monster) + && CheckCalledbytheGrave(CardId.AriannaTheLabrynthServant) == 0 && arianna != null && !Bot.HasInHand(CardId.AriannaTheLabrynthServant)) + { + return Util.CheckSelectCount(new List { arianna }, cards, min, max); + } + + // search not exist furniture + if (!lackUnimportantCost) + { + foreach (int checkId in furnitureCheckIdList) + { + ClientCard furniture = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && furniture != null) + { + if (checkId == CardId.LabrynthCooclock) + { + if (Enemy.GetMonsterCount() > 0 && !Bot.HasInHandOrInMonstersZoneOrInGraveyard(CardId.LabrynthChandraglier)) continue; + } + return Util.CheckSelectCount(new List { furniture }, cards, min, max); + } + } + } + + // search not exist card + List uniqueCheckIdList = new List{ + CardId.BigWelcomeLabrynth, CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.LabrynthChandraglier, + CardId.LadyLabrynthOfTheSilverCastle, CardId.AriasTheLabrynthButler, CardId.ArianeTheLabrynthServant, CardId.WelcomeLabrynth}; + foreach (int checkId in uniqueCheckIdList) + { + ClientCard targetCard = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (!Bot.HasInMonstersZone(checkId) && !Bot.HasInHandOrInSpellZone(checkId) && targetCard != null) + { + if (checkId == CardId.BigWelcomeLabrynth || checkId == CardId.WelcomeLabrynth) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, checkId) }, cards, min, max); + } + else + { + return Util.CheckSelectCount(new List { targetCard }, cards, min, max); + } + } + } + + foreach (int checkId in uniqueCheckIdList) + { + ClientCard targetCard = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (CheckRemainInDeck(checkId) > 0) + { + if (checkId == CardId.BigWelcomeLabrynth || checkId == CardId.WelcomeLabrynth) + { + return Util.CheckSelectCount(new List { AriannaSearchWelcomeTrap(cards, checkId) }, cards, min, max); + } + else + { + return Util.CheckSelectCount(new List { targetCard }, cards, min, max); + } + } + } + } + + // solved when have more than 1 monster + if (bigwelcomeSoving && hint == HintMsg.SpSummon) + { + bool activateTimingFlag = Duel.Phase > DuelPhase.Main2 || (Card.IsCode(CardId.AriasTheLabrynthButler) && (CurrentTiming & hintTimingMainEnd) > 0); + + bool needDestroyFlag = GetProblematicEnemyCardList(false).Count() > 0; + needDestroyFlag |= activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && activateTimingFlag; + needDestroyFlag |= Bot.UnderAttack && (Bot.BattlingMonster?.GetDefensePower() ?? 0) <= (Enemy.BattlingMonster?.GetDefensePower() ?? 0) && Duel.LastChainPlayer != 0; + needDestroyFlag |= Duel.Turn == 1 && Duel.Player == 0 && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + needDestroyFlag |= Duel.Turn == 1 && Enemy.GetMonsterCount() == 0 && Enemy.GetSpellCount() == 0 && Enemy.Hand.Count > 0 + && (CurrentTiming & hintTimingMainEnd) > 0; + + if (needDestroyFlag && cards.Any(c => c.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) + && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.LovelyLabrynthOfTheSilverCastle) }, cards, min, max); + } + if (cards.Any(c => c.IsCode(CardId.AriannaTheLabrynthServant)) + && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !Bot.HasInMonstersZone(CardId.AriannaTheLabrynthServant)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.AriannaTheLabrynthServant) }, cards, min, max); + } + if (cards.Any(c => c.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) + && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.LovelyLabrynthOfTheSilverCastle) }, cards, min, max); + } + if (cards.Any(c => c.IsCode(CardId.AriannaTheLabrynthServant)) + && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.AriannaTheLabrynthServant) }, cards, min, max); + } + if (cards.Any(c => c.IsCode(CardId.LadyLabrynthOfTheSilverCastle)) + && Duel.Turn > 1 && Duel.Phase < DuelPhase.Main2 && Duel.Player == 0 && Enemy.GetMonsterCount() == 0) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, CardId.LadyLabrynthOfTheSilverCastle) }, cards, min, max); + } + List furnitureCheckIdList = new List { CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.LabrynthChandraglier, CardId.AriasTheLabrynthButler }; + foreach (int furniture in furnitureCheckIdList) + { + if (cards.Any(c => c.IsCode(furniture)) && !Bot.HasInHandOrInMonstersZoneOrInGraveyard(furniture)) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, furniture) }, cards, min, max); + } + } + List checkIdList = new List{CardId.ArianeTheLabrynthServant, CardId.LadyLabrynthOfTheSilverCastle, CardId.AriannaTheLabrynthServant, + CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.LabrynthChandraglier, CardId.AriasTheLabrynthButler}; + foreach (int checkId in checkIdList) + { + if (cards.Any(c => c.IsCode(checkId))) + { + return Util.CheckSelectCount(new List { GetWelcomeOrBigWelcomeTarget(cards, checkId) }, cards, min, max); + } + } + // should not get here + Logger.DebugWriteLine("[warning] call BigWelcomeSpSummon with no select."); + } + + if (bigwelcomeSoving && hint == HintMsg.ReturnToHand) + { + if (bigwelcomeEscaseTarget != null && cards.Contains(bigwelcomeEscaseTarget)) + { + return Util.CheckSelectCount(new List { bigwelcomeEscaseTarget }, cards, min, max); + } + + ClientCard cooclock = cards.FirstOrDefault(c => c.IsCode(CardId.LabrynthCooclock)); + bool canSearchWelcome = CheckRemainInDeck(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth) > 0 + && (Bot.HasInHandOrHasInMonstersZone(new List { CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie }) + || summonInChainList.Any(c => c.IsCode(CardId.AriannaTheLabrynthServant)) + || (Duel.Player == 0 && !summoned && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && Bot.HasInHand(CardId.AriannaTheLabrynthServant))); + if (cooclock != null) + { + if (setTrapThisTurn.Count() > 0 + || (Duel.Turn == 1 && ( + (!activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle) && Bot.HasInHandOrHasInMonstersZone(CardId.LadyLabrynthOfTheSilverCastle)) + || canSearchWelcome + ) + ) + || Duel.Turn == 0 && canSearchWelcome + ) + return Util.CheckSelectCount(new List { cooclock }, cards, min, max); + } + + ClientCard defenseLady = cards.FirstOrDefault(c => c.IsDefense() && c.IsCode(CardId.LadyLabrynthOfTheSilverCastle)); + ClientCard attackLady = cards.FirstOrDefault(c => c.IsAttack() && c.IsCode(CardId.LadyLabrynthOfTheSilverCastle)); + if (Bot.GetMonsters().Any(card => (Duel.Player == 1 || card.IsDefense()) && card.IsCode(CardId.LadyLabrynthOfTheSilverCastle)) + && (!activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle) || activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle + 1))) + { + if (defenseLady != null) return Util.CheckSelectCount(new List { defenseLady }, cards, min, max); + if (attackLady != null) return Util.CheckSelectCount(new List { attackLady }, cards, min, max); + } + if (summonInChainList.Any(c => c.IsCode(CardId.LovelyLabrynthOfTheSilverCastle))) + { + List returnCheckIdList = new List{ + _CardId.MaxxC, CardId.AriannaTheLabrynthServant, _CardId.AshBlossom, CardId.LabrynthCooclock, CardId.LadyLabrynthOfTheSilverCastle, + CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, CardId.AriasTheLabrynthButler, CardId.UnchainedSoulOfSharvara, CardId.ArianeTheLabrynthServant + }; + foreach (int checkId in returnCheckIdList) + { + ClientCard returnTarget = cards.FirstOrDefault(c => c.IsCode(checkId)); + if (returnTarget != null) return Util.CheckSelectCount(new List { returnTarget }, cards, min, max); + } + return Util.CheckSelectCount(cards.OrderBy(card => card.Attack).ToList(), cards, min, max); + } + if (cards.Count() == 1) return Util.CheckSelectCount(cards.OrderBy(card => card.Attack).ToList(), cards, min, max); + ClientCard ariannaNotSummon = cards.FirstOrDefault(c => c.IsCode(CardId.AriannaTheLabrynthServant) && !summonInChainList.Contains(c)); + if (ariannaNotSummon != null) return Util.CheckSelectCount(new List { ariannaNotSummon }, cards, min, max); + else + { + // compare which have lower attack + ClientCard fieldTarget = Bot.GetMonsters().Where(card => !card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) + .OrderBy(card => card.Attack).FirstOrDefault(); + if (fieldTarget != null) return Util.CheckSelectCount(new List { fieldTarget }, cards, min, max); + } + } + } + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + public ClientCard GetWelcomeOrBigWelcomeTarget(IList cards, int cardId) + { + ClientCard graveTarget = cards.FirstOrDefault(card => card.IsCode(cardId) && card.Location == CardLocation.Grave); + if (graveTarget != null) return graveTarget; + ClientCard deckTarget = cards.FirstOrDefault(card => card.IsCode(cardId) && card.Location == CardLocation.Deck); + if (deckTarget != null) return deckTarget; + ClientCard handTarget = cards.FirstOrDefault(card => card.IsCode(cardId) && card.Location == CardLocation.Hand); + if (handTarget != null) return handTarget; + return null; + } + + public ClientCard AriannaSearchWelcomeTrap(IList cards, int welcomeId) + { + bool haveCostToSolve = Bot.HasInHand(new List { CardId.LovelyLabrynthOfTheSilverCastle, CardId.TransactionRollback, CardId.AriasTheLabrynthButler }); + if (haveCostToSolve) + { + List checkIdList = new List { CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier }; + foreach (int checkId in checkIdList) + { + ClientCard targetCard = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (targetCard != null && !Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && CheckCalledbytheGrave(checkId) == 0 && !activatedCardIdList.Contains(checkId)) + { + return targetCard; + } + } + foreach (int checkId in checkIdList) + { + ClientCard targetCard = GetWelcomeOrBigWelcomeTarget(cards, checkId); + if (targetCard != null && CheckCalledbytheGrave(checkId) == 0 && !activatedCardIdList.Contains(checkId)) + { + return targetCard; + } + } + } + + return GetWelcomeOrBigWelcomeTarget(cards, welcomeId); + } + + /// + /// go first + /// + public override bool OnSelectHand() + { + return true; + } + + public override bool OnSelectMonsterSummonOrSet(ClientCard card) + { + if (card.Attack > 0 && CheckCanDirectAttack()) return false; + if (card.Attack <= 1000) return true; + + return base.OnSelectMonsterSummonOrSet(card); + } + + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone) + { + // selected in summon process + if (cardId == CardId.RelinquishedAnima) + { + return base.OnSelectPlace(cardId, player, location, available); + } + if (cardId == CardId.UnchainedSoulLordOfYama || cardId == CardId.UnchainedSoulOfAnguish) + { + if (Bot.MonsterZone[0] != null && Bot.MonsterZone[2] != null && (Zones.z6 & available) != 0) return Zones.z6; + if (Bot.MonsterZone[2] != null && Bot.MonsterZone[4] != null && (Zones.z5 & available) != 0) return Zones.z5; + } + if (cardId == CardId.MuckrakerFromTheUnderworld || cardId == CardId.UnchainedSoulOfRage) + { + if (Bot.MonsterZone[1] != null && (Zones.z6 & available) != 0) return Zones.z6; + if (Bot.MonsterZone[3] != null && (Zones.z5 & available) != 0) return Zones.z5; + } + + List zoneIdList = ShuffleList(new List { 0, 2, 4 }); + zoneIdList.AddRange(ShuffleList(new List { 1, 3 })); + zoneIdList.AddRange(ShuffleList(new List { 5, 6 })); + foreach (int zoneId in zoneIdList) + { + int zone = (int)System.Math.Pow(2, zoneId); + if ((available & zone) != 0 && Bot.MonsterZone[zoneId] == null) + { + return zone; + } + } + } + return base.OnSelectPlace(cardId, player, location, available); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2) + { + bool turnDefense = false; + if (cardData.Attack <= cardData.Defense) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } + int cardAttack = cardData.Attack; + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardAttack); + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + public override int OnSelectOption(IList options) + { + // override for cooclock + if (options.Count() == 2 && options.Contains(1190) && options.Contains(1152)) + { + // 1190=Add to Hand, 1152=Special Summon + // return to hand to activate trap set this turn + bool canLink = Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2; + if (!canLink && !Bot.HasInHand(CardId.LabrynthCooclock) && Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)) + && !activatedCardIdList.Contains(CardId.LabrynthCooclock) && !CheckWhetherWillbeRemoved() + && (activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) || Bot.GetSpells().All(card => setTrapThisTurn.Contains(card) || !card.IsCode(CardId.BigWelcomeLabrynth))) + && setTrapThisTurn.Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth, _CardId.DimensionalBarrier, _CardId.InfiniteImpermanence, CardId.DestructiveDarumaKarmaCannon))) + { + return options.IndexOf(1190); + } + if (!enemyActivateMaxxC) return options.IndexOf(1152); + if (activatedCardIdList.Contains(CardId.LabrynthCooclock)) + { + if (!CheckShouldNoMoreSpSummon()) return options.IndexOf(1152); + } + return options.IndexOf(1190); + } + + // overrride for lovely + if (options.Contains(Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 3)) && options.Contains(Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 4))) + { + int botWorstAttack = 0; + ClientCard botWorstMonster = Util.GetWorstBotMonster(true); + if (botWorstMonster != null) + { + botWorstAttack = botWorstMonster.Attack; + } + List targetList = GetProblematicEnemyCardList(false); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && card.GetDefensePower() >= botWorstAttack && !currentDestroyCardList.Contains(card)).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => !currentDestroyCardList.Contains(card) && card.IsFacedown()).ToList())); + if (targetList.Count() > 0) + { + currentDestroyCardList.Add(targetList[0]); + AI.SelectCard(targetList); + return options.IndexOf(Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 4)); + } + else + { + return options.IndexOf(Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 3)); + } + } + + // override for dimensional barrier + if (options.IndexOf(HintMsg.RITUAL) >= 0 || options.IndexOf(HintMsg.FUSION) >= 0 || options.IndexOf(HintMsg.SYNCHRO) >= 0 + || options.IndexOf(HintMsg.XYZ) >= 0 || options.IndexOf(HintMsg.PENDULUM) >= 0) + { + Dictionary> barrierCheckDict = new Dictionary> + { + {HintMsg.RITUAL, DimensionalBarrierForRitual}, + {HintMsg.FUSION, DimensionalBarrierForFusion}, + {HintMsg.SYNCHRO, DimensionalBarrierForSynchro}, + {HintMsg.XYZ, DimensionalBarrierForXyz}, + {HintMsg.PENDULUM, DimensionalBarrierForPendulum}, + }; + dimensionBarrierAnnouncing = true; + foreach (KeyValuePair> checkPair in barrierCheckDict) + { + if (options.Contains(checkPair.Key) && checkPair.Value()) + { + dimensionBarrierAnnouncing = false; + dimensionalBarrierAnnouced.Add(checkPair.Key); + return options.IndexOf(checkPair.Key); + } + } + dimensionBarrierAnnouncing = false; + List enemyMonsterList = new List(Enemy.GetMonsters()); + enemyMonsterList.AddRange(Enemy.GetGraveyardMonsters()); + Dictionary barrierCheckSecondark = new Dictionary + { + {HintMsg.RITUAL, enemyMonsterList.Any(card => card.HasType(CardType.Ritual))}, + {HintMsg.FUSION, enemyMonsterList.Any(card => card.HasType(CardType.Fusion))}, + {HintMsg.SYNCHRO, enemyMonsterList.Any(card => card.HasType(CardType.Synchro))}, + {HintMsg.XYZ, enemyMonsterList.Any(card => card.HasType(CardType.Xyz))}, + {HintMsg.PENDULUM, enemyMonsterList.Any(card => card.HasType(CardType.Pendulum))}, + }; + foreach (KeyValuePair checkPair in barrierCheckSecondark) + { + if (options.Contains(checkPair.Key) && checkPair.Value) + { + dimensionBarrierAnnouncing = false; + dimensionalBarrierAnnouced.Add(checkPair.Key); + return options.IndexOf(checkPair.Key); + } + } + List annouceList = new List { HintMsg.XYZ, HintMsg.SYNCHRO, HintMsg.FUSION, HintMsg.PENDULUM, HintMsg.RITUAL }; + foreach (int annouce in annouceList) + { + if (options.Contains(annouce)) + { + return options.IndexOf(annouce); + } + } + } + + // override for servant + // sp summon + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 2)) || options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 2))) + { + if (GetEmptyMainMonsterZoneCount() > chainSummoningIdList.Count()) + { + bool checkFlag = false; + if (!activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && Bot.HasInHand(CardId.AriannaTheLabrynthServant) + && !CheckWhetherNegated(true, true, CardType.Monster) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + checkFlag = true; + AI.SelectCard(CardId.AriannaTheLabrynthServant); + } + if (!checkFlag) + { + List checkIdList = new List { CardId.LovelyLabrynthOfTheSilverCastle, CardId.LadyLabrynthOfTheSilverCastle }; + foreach (int checkId in checkIdList) + { + if (Bot.HasInHand(checkId)) + { + checkFlag = true; + AI.SelectCard(checkId); + break; + } + } + } + if (!checkFlag && Duel.Player == 0 && Duel.Phase < DuelPhase.End) + { + List linkMaterialList = GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)); + if (linkMaterialList.Count() + chainSummoningIdList.Count() == 2) + { + // summon less atk for link + ClientCard selected = null; + int attack = 0; + foreach (ClientCard hand in Bot.Hand) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(hand.Id); + if (cardData == null || cardData.Race != (int)CardRace.Fiend) continue; + if (selected == null || attack > hand.Attack) + { + selected = hand; + attack = hand.Attack; + } + } + if (selected != null) + { + checkFlag = true; + AI.SelectCard(selected); + } + } + } + if (!checkFlag && CheckCanDirectAttack()) + { + // summon best attack for attack + ClientCard selected = null; + int attack = 0; + foreach (ClientCard hand in Bot.Hand) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(hand.Id); + if (cardData == null || cardData.Race != (int)CardRace.Fiend) continue; + if (selected == null || attack < hand.Attack) + { + selected = hand; + attack = hand.Attack; + } + } + if (selected != null) + { + checkFlag = true; + AI.SelectCard(selected); + } + } + + if (checkFlag) + { + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 2))) return options.IndexOf(Util.GetStringId(CardId.AriannaTheLabrynthServant, 2)); + if (options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 2))) return options.IndexOf(Util.GetStringId(CardId.ArianeTheLabrynthServant, 2)); + } + } + } + + // set + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 3)) || options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 3))) + { + if (!Util.ChainContainsCard(CardId.WelcomeLabrynth) || Bot.GetSpellCountWithoutField() < 4) + { + List checkIdList = new List { CardId.BigWelcomeLabrynth, CardId.WelcomeLabrynth, _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon }; + foreach (int checkId in checkIdList) + { + if (Bot.HasInHand(checkId) && (checkId == _CardId.InfiniteImpermanence || !Bot.HasInSpellZone(checkId, faceUp:false))) + { + AI.SelectCard(checkId); + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 3))) return options.IndexOf(Util.GetStringId(CardId.AriannaTheLabrynthServant, 3)); + if (options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 3))) return options.IndexOf(Util.GetStringId(CardId.ArianeTheLabrynthServant, 3)); + } + } + } + } + + // do nothing + if (options.Contains(Util.GetStringId(CardId.AriannaTheLabrynthServant, 4))) return options.IndexOf(Util.GetStringId(CardId.AriannaTheLabrynthServant, 4)); + if (options.Contains(Util.GetStringId(CardId.ArianeTheLabrynthServant, 4))) return options.IndexOf(Util.GetStringId(CardId.ArianeTheLabrynthServant, 4)); + + return base.OnSelectOption(options); + } + + public override bool OnSelectYesNo(long desc) + { + if (desc == 96) + { + Logger.DebugWriteLine("*** muckraker replace."); + AI.SelectCard(Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasRace(CardRace.Fiend)).OrderBy(card => card.Attack).ToList()); + return true; + } + return base.OnSelectYesNo(desc); + } + + public override void OnNewTurn() + { + if (Duel.Turn <= 1) + { + dimensionShifterCount = 0; + + enemySpSummonFromExLastTurn = 0; + enemySpSummonFromExThisTurn = 0; + banSpSummonExceptFiendCount = 0; + } + enemyActivateMaxxC = false; + enemySpSummonFromExLastTurn = enemySpSummonFromExThisTurn; + enemySpSummonFromExThisTurn = 0; + rollbackCopyCardId = 0; + + if (dimensionShifterCount > 0) dimensionShifterCount--; + if (banSpSummonExceptFiendCount > 0) banSpSummonExceptFiendCount--; + infiniteImpermanenceList.Clear(); + + summoned = false; + cooclockAffected = false; + activatedCardIdList.Clear(); + setTrapThisTurn.Clear(); + summonThisTurn.Clear(); + enemySetThisTurn.Clear(); + dimensionalBarrierAnnouced.Clear(); + summonInChainList.Clear(); + base.OnNewTurn(); + } + + public override void OnChaining(int player, ClientCard card) + { + if (card == null) return; + if (chainSummoningIdList.Count() > 0) + Logger.DebugWriteLine("[Welcome] Summoning: " + string.Join(",", chainSummoningIdList) + "\n"); + + if (player == 1) + { + if (card.IsCode(_CardId.InfiniteImpermanence)) + { + if (enemyActivateInfiniteImpermanenceFromHand) + { + enemyActivateInfiniteImpermanenceFromHand = false; + } + else { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + infiniteImpermanenceList.Add(4-i); + break; + } + } + } + } + } + base.OnChaining(player, card); + } + + public override void OnChainSolved(int chainIndex) + { + ChainInfo currentCard = Duel.GetCurrentSolvingChainInfo(); + if (currentCard != null && !Duel.IsCurrentSolvingChainNegated()) + { + if (currentCard.ActivatePlayer == 1) + { + if (currentCard.IsCode(_CardId.MaxxC)) + enemyActivateMaxxC = true; + if (currentCard.IsCode(CardId.DimensionShifter)) + dimensionShifterCount = 2; + } + if (currentCard.ActivatePlayer == 0) + { + if (currentCard.IsCode(CardId.LabrynthCooclock)) + cooclockAffected = true; + } + } + + base.OnChainSolved(chainIndex); + } + + public override void OnChainEnd() + { + rollbackCopyCardId = 0; + currentNegateMonsterList.Clear(); + currentDestroyCardList.Clear(); + escapeTargetList.Clear(); + chainSummoningIdList.Clear(); + summonInChainList.Clear(); + enemyActivateInfiniteImpermanenceFromHand = false; + for (int idx = enemySetThisTurn.Count() - 1; idx >= 0; idx --) + { + ClientCard checkTarget = enemySetThisTurn[idx]; + if (checkTarget == null || checkTarget.Location != CardLocation.SpellZone || checkTarget.HasPosition(CardPosition.FaceUp)) + { + enemySetThisTurn.RemoveAt(idx); + } + } + if (cooclockActivating) + cooclockActivating = false; + furnitureActivating = false; + dimensionBarrierAnnouncing = false; + bigwelcomeEscaseTarget = null; + base.OnChainEnd(); + } + + public override void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + if (previousControler == 1) + { + if (previousLocation == (int)CardLocation.Extra && currentLocation == (int)CardLocation.MonsterZone) enemySpSummonFromExThisTurn ++; + if (card != null) + { + if (card.IsCode(_CardId.InfiniteImpermanence) && previousLocation == (int)CardLocation.Hand && currentLocation == (int)CardLocation.SpellZone) + enemyActivateInfiniteImpermanenceFromHand = true; + if (card.Location == CardLocation.SpellZone && card.HasPosition(CardPosition.FaceDown)) + enemySetThisTurn.Add(card); + } + } + if (card != null) + { + if (previousControler == 0) + { + if (previousLocation == (int)CardLocation.MonsterZone && currentLocation != (int)CardLocation.MonsterZone) + { + if (summonThisTurn.Contains(card)) summonThisTurn.Remove(card); + if (summonInChainList.Contains(card)) summonInChainList.Remove(card); + } + if (previousLocation == (int)CardLocation.SpellZone && currentLocation != (int)CardLocation.SpellZone) + { + if (setTrapThisTurn.Contains(card)) setTrapThisTurn.Remove(card); + } + } + if (currentControler == 0) + { + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (currentLocation == (int)CardLocation.SpellZone && (currentSolvingChain == null || !currentSolvingChain.IsCode(CardId.AriasTheLabrynthButler)) + && (card.HasType(CardType.Trap) || card.IsCode(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth)) + ) + { + Logger.DebugWriteLine("[setTrapThisTurn]set " + card.Name ?? "UnknowCard"); + setTrapThisTurn.Add(card); + } + if (currentLocation == (int)CardLocation.MonsterZone) + { + summonThisTurn.Add(card); + if (currentSolvingChain != null) summonInChainList.Add(card); + } + } + } + + base.OnMove(card, previousControler, previousLocation, currentControler, currentLocation); + } + + public override BattlePhaseAction OnBattle(IList attackers, IList defenders) + { + if (attackers.Count() > 0 && defenders.Count() > 0) + { + List sortedAttacker = attackers.OrderBy(card => card.Attack).ToList(); + for (int k = 0; k < sortedAttacker.Count; ++k) + { + ClientCard attacker = sortedAttacker[k]; + attacker.IsLastAttacker = k == sortedAttacker.Count - 1; + BattlePhaseAction result = OnSelectAttackTarget(attacker, defenders); + if (result != null) + return result; + } + } + + return base.OnBattle(attackers, defenders); + } + + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public void ResetCooclockEffect(bool onlyCheck) + { + if (!onlyCheck && cooclockAffected && setTrapThisTurn.Contains(Card)) + { + cooclockAffected = false; + setTrapThisTurn.Remove(Card); + } + } + + + + + + public bool LadyLabrynthOfTheSilverCastleFieldActivate() + { + if (Card.Location == CardLocation.MonsterZone && (Util.GetLastChainCard() == null || !Util.GetLastChainCard().IsCode(_CardId.EvenlyMatched)) + && (!CheckWhetherNegated() || Enemy.HasInMonstersZone(CardId.LadyLabrynthOfTheSilverCastle))) + { + activatedCardIdList.Add(Card.Id + 1); + return true; + } + return false; + } + + public bool LadyLabrynthOfTheSilverCastleHandActivate() + { + if (Card.Location == CardLocation.Hand) + { + // sp summon from hand + if (CheckShouldNoMoreSpSummon(true) || Util.ChainContainsCard(_CardId.EvenlyMatched)) return false; + bool activateFlag = false; + activateFlag |= CheckChainContainEnemyMaxxC(); + if (!activateFlag && GetEmptyMainMonsterZoneCount() + chainSummoningIdList.Count() <= 0) + { + return false; + } + activateFlag |= cooclockAffected && setTrapThisTurn.Count() > 0 && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)); + activateFlag |= Bot.GetSpells().Any(card => card.IsFacedown() && card.HasType(CardType.Trap) && !setTrapThisTurn.Contains(card)) + && !Bot.HasInMonstersZone(Card.Id, true, faceUp: true); + activateFlag |= Duel.Player == 1 && Duel.Phase >= DuelPhase.End; + activateFlag |= setTrapThisTurn.Count() > 0 && Duel.Phase >= DuelPhase.End; + activateFlag |= Bot.UnderAttack && Bot.GetMonsterCount() == 0 && !Util.ChainContainsCard(CardId.DestructiveDarumaKarmaCannon); + // for link + if (Bot.HasInExtra(CardId.UnchainedSoulLordOfYama) && Duel.Player == 0 && Duel.Phase < DuelPhase.End) + { + // check whether need summon for material count + List materialList = GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)); + int materialCount = materialList.Count(); + if (!activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara) && Bot.GetSpells().Any(card => card.IsFacedown()) + && (!activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama) || Bot.HasInHand(CardId.UnchainedSoulOfSharvara))) materialCount++; + if ( (materialCount == 2 || materialCount == 1 && materialList.Any(card => card.IsCode(CardId.UnchainedSoulLordOfYama))) + && Bot.HasInExtra(CardId.UnchainedSoulLordOfYama) || materialList.Any(card => card.HasSetcode(SetcodeUnchained))) + { + activateFlag |= Enemy.GetMonsterCount() > 0 && Bot.HasInExtra(CardId.UnchainedSoulOfAnguish); + activateFlag |= Bot.HasInExtra(CardId.UnchainedSoulOfRage); + } + } + + if (activateFlag) + { + activatedCardIdList.Add(Card.Id); + chainSummoningIdList.Add(Card.Id); + return true; + } + } + return false; + } + + public bool LovelyLabrynthOfTheSilverCastleActivate() + { + if (CheckWhetherNegated()) return false; + if (ActivateDescription == Util.GetStringId(CardId.LovelyLabrynthOfTheSilverCastle, 0)) + { + // set from GY + List checkIdList = new List{CardId.BigWelcomeLabrynth, _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, + CardId.DestructiveDarumaKarmaCannon, CardId.WelcomeLabrynth}; + foreach (int checkId in checkIdList) + { + if (Bot.HasInGraveyard(checkId) && !Bot.HasInHandOrInSpellZone(checkId)) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + return true; + } + } + foreach (int checkId in checkIdList) + { + if (Bot.HasInGraveyard(checkId)) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + if (GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)).Count() == 2 + && !activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama) + && (Bot.HasInGraveyard(CardId.UnchainedSoulOfSharvara) || CheckRemainInDeck(CardId.UnchainedSoulOfSharvara) > 0)) + { + activatedCardIdList.Add(Card.Id); + return true; + } + } + else + { + // destroy + if (Enemy.GetHandCount() == 0) + { + int botWorstAttack = 0; + ClientCard botWorstMonster = Util.GetWorstBotMonster(true); + if (botWorstMonster != null) + { + botWorstAttack = botWorstMonster.Attack; + } + List targetList = GetProblematicEnemyCardList(false); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && card.GetDefensePower() >= botWorstAttack && !currentDestroyCardList.Contains(card)).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => !currentDestroyCardList.Contains(card)).ToList())); + + if (targetList.Count() > 0) + { + currentDestroyCardList.Add(targetList[0]); + AI.SelectCard(targetList); + AI.SelectOption(1); + } + else + { + AI.SelectOption(0); + } + } + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool UnchainedSoulOfSharvaraActivate() + { + // search + if (Card.Location == CardLocation.Grave) + { + activatedCardIdList.Add(Card.Id + 1); + SelectSTPlace(null, false); + return true; + } + + // sp summon + if (Bot.HasInSpellZone(CardId.TransactionRollback) && GetEmptyMainMonsterZoneCount() > chainSummoningIdList.Count() + && !CheckWhetherWillbeRemoved() && !CheckShouldNoMoreSpSummon(false)) + { + AI.SelectCard(CardId.TransactionRollback); + activatedCardIdList.Add(Card.Id); + return true; + } + + // escape target + if (Duel.LastChainPlayer == 1) + { + ClientCard chainCard = Util.GetLastChainCard(); + if (chainCard != null && chainCard.IsCode(targetNegateIdList)) + { + if (Duel.LastChainTargets.Any(card => card.Controller == 0 && card.IsFaceup() && card.HasRace(CardRace.Fiend) + && Duel.CurrentChain.Any(chain => chain == card) && !card.IsCode(CardId.UnchainedSoulOfRage, CardId.UnchainedSoulOfAnguish))) + { + escapeTargetList.AddRange(Duel.LastChainTargets); + AI.SelectCard(Duel.LastChainTargets); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + // for link + bool destroySpells = Duel.Player == 0 && GetEmptyMainMonsterZoneCount() > chainSummoningIdList.Count() && Bot.GetMonsterCount() > 0 && CurrentTiming <= 0; + if (destroySpells) + { + List materialList = GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)); + destroySpells = CheckAtAdvantage() && !Bot.HasInMonstersZone(CardId.UnchainedSoulOfRage) + && Bot.HasInExtra(CardId.UnchainedSoulOfRage) && materialList.Count() == 1; + if (Bot.HasInExtra(CardId.UnchainedSoulOfAnguish) && !Bot.HasInMonstersZone(CardId.UnchainedSoulOfAnguish) + && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish) && Enemy.GetMonsters().Where(card => card.IsFaceup()).Count() > 0) + { + destroySpells |= materialList.Count() == 2; + destroySpells |= materialList.Count() == 1 && materialList.Any(card => card.HasType(CardType.Link) && card.LinkCount == 2); + } + } + // for attack + destroySpells |= CheckCanDirectAttack() && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack() + 2000 >= Enemy.LifePoints + && GetEmptyMainMonsterZoneCount() > chainSummoningIdList.Count(); + // for avoid lose + destroySpells |= Duel.Player == 1 && Duel.Phase == DuelPhase.Main1 && Bot.GetMonsterCount() == 0 && (CurrentTiming & hintTimingMainEnd) != 0 + && Util.GetTotalAttackingMonsterAttack(1) >= Bot.LifePoints; + if (destroySpells) + { + List destroyIdList = new List{_CardId.InfiniteImpermanence, CardId.TransactionRollback, CardId.WelcomeLabrynth, + _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon, CardId.BigWelcomeLabrynth}; + foreach (int checkId in destroyIdList) + { + ClientCard target = Bot.GetSpells().FirstOrDefault(card => card.IsFacedown() && card.IsCode(checkId)); + if (target != null) + { + AI.SelectCard(target); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + return false; + } + + public bool AriasTheLabrynthButlerActivate() + { + if (Card.Location != CardLocation.Grave) + { + if (Util.ChainContainsCard(new int[] { _CardId.DivineArsenalAAZEUS_SkyThunder, _CardId.EvenlyMatched, _CardId.EvilswarmExcitonKnight })) return false; + if (Duel.CurrentChain.Any(card => card.Controller == 0 && card.IsCode(CardId.AriannaTheLabrynthServant))) return false; + + // set trap to activate + SortedList> checkList = new SortedList> { + {CardId.BigWelcomeLabrynth, BigWelcomeLabrynthSetCheck}, + {CardId.WelcomeLabrynth, WelcomeLabrynthSetCheck}, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonSetCheck}, + {_CardId.DimensionalBarrier, DimensionalBarrierActivate} + }; + foreach (KeyValuePair> pair in checkList) + { + ClientCard setTarget = Bot.Hand.FirstOrDefault(card => card.IsCode(pair.Key)); + if (setTarget != null && !activatedCardIdList.Contains(pair.Key) && pair.Value()) + { + AI.SelectOption(1); + AI.SelectCard(pair.Key); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(setTarget, true); + return true; + } + } + + // special summon monster + if (Bot.HasInHand(CardId.LovelyLabrynthOfTheSilverCastle)) + { + // before main end + if (Duel.Player == 0 || (CurrentTiming & hintTimingMainEnd) != 0) + { + AI.SelectOption(0); + AI.SelectCard(CardId.LovelyLabrynthOfTheSilverCastle); + chainSummoningIdList.Add(CardId.LovelyLabrynthOfTheSilverCastle); + activatedCardIdList.Add(Card.Id); + return true; + } + } + if (Bot.HasInHand(CardId.AriannaTheLabrynthServant) && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) + && !CheckWhetherNegated(true, true) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + bool searchFlag = false; + if (Duel.Player == 1) + { + searchFlag |= (CurrentTiming & hintTimingMainEnd) != 0; + searchFlag |= GetProblematicEnemyCardList(false).Count() > 0 + && (Bot.HasInMonstersZoneOrInGraveyard(CardId.LovelyLabrynthOfTheSilverCastle) || CheckRemainInDeck(CardId.LovelyLabrynthOfTheSilverCastle) > 0) + && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + } + if (Duel.Player == 0) searchFlag |= summoned && !CheckShouldNoMoreSpSummon(); + if (searchFlag) + { + AI.SelectOption(0); + AI.SelectCard(CardId.AriannaTheLabrynthServant); + chainSummoningIdList.Add(CardId.AriannaTheLabrynthServant); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + return false; + } + + public bool ArianeTheLabrynthServantSummon() + { + // for attack + if (Duel.Turn > 1 && Enemy.GetMonsterCount() == 0) + { + summoned = true; + return true; + } + // for activate effect + if (!activatedCardIdList.Contains(Card.Id) && !CheckWhetherNegated(true, true) && !CheckWhetherWillbeRemoved()) + { + bool haveCost = Bot.Hand.Any(card => card.Type == (int)CardType.Trap) || Bot.GetSpells().Any(card => card.IsFacedown() && card.Type == (int)CardType.Trap); + if (haveCost && !CheckShouldNoMoreSpSummon(true)) + { + summoned = true; + return true; + } + } + + return false; + } + public bool ArianeTheLabrynthServantForRollbackSummon() + { + if (activatedCardIdList.Contains(Card.Id)) return false; + if (Bot.HasInHandOrInSpellZone(CardId.TransactionRollback) && !CheckWhetherWillbeRemoved()) + { + summoned = true; + return true; + } + + return false; + } + public bool ArianeTheLabrynthServantActivate() + { + // special summon + if (ActivateDescription == Util.GetStringId(Card.Id, 0)) + { + bool haveRollback = Bot.HasInHandOrInSpellZone(CardId.TransactionRollback); + if (CheckWhetherNegated() && !haveRollback) return false; + if (CheckShouldNoMoreSpSummon() && !(haveRollback && Bot.Graveyard.Any(card => card.IsCode(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth)))) return false; + int specialSummonId = 0; + // arianna + if (!activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && CheckRemainInDeck(CardId.AriannaTheLabrynthServant) > 0) + { + specialSummonId = CardId.AriannaTheLabrynthServant; + } + // sp summon not used furniture + if (specialSummonId == 0) + { + List checkIdList = new List{CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock}; + foreach (int checkId in checkIdList) + { + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && CheckRemainInDeck(checkId) > 0) + { + specialSummonId = checkId; + break; + } + } + } + // for attack/link + if (specialSummonId == 0) + { + List checkIdList = new List(); + if (Enemy.GetMonsterCount() == 0) checkIdList.AddRange(new List{ + CardId.AriannaTheLabrynthServant, CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock + }); + else checkIdList.AddRange(new List{ + CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, CardId.LabrynthCooclock, CardId.AriannaTheLabrynthServant + }); + foreach (int checkId in checkIdList) + { + if (CheckRemainInDeck(checkId) > 0) + { + specialSummonId = checkId; + break; + } + } + } + + if (specialSummonId > 0) + { + bool costSelected = false; + if (haveRollback) + { + AI.SelectCard(CardId.TransactionRollback); + costSelected = true; + } + if (!costSelected) { + ClientCard welcome = Bot.GetSpells().FirstOrDefault(card => card.IsCode(CardId.WelcomeLabrynth)); + if (welcome != null) + { + AI.SelectCard(welcome); + costSelected = true; + } + } + List costCheckList = Bot.Hand.Where(card => card.IsFacedown() && card.Type == (int)CardType.Trap).ToList(); + costCheckList.AddRange(Bot.GetSpells().Where(card => card.IsFacedown() && card.Type == (int)CardType.Trap).ToList()); + if (!costSelected) + { + List checkIdList = new List{_CardId.InfiniteImpermanence, CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth}; + foreach (int checkId in checkIdList) + { + ClientCard dumpCard = costCheckList.FirstOrDefault(card => card.IsCode(checkId)); + if (costCheckList.Count(card => card.IsCode(checkId)) > 1 && dumpCard != null) + { + AI.SelectCard(dumpCard); + costSelected = true; + break; + } + } + } + if (!costSelected) + { + List checkIdList = new List{_CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon, + CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth}; + foreach (int checkId in checkIdList) + { + ClientCard checkCard = costCheckList.FirstOrDefault(card => card.IsCode(checkId)); + if (checkCard != null) + { + AI.SelectCard(checkCard); + costSelected = true; + break; + } + } + } + } + } else { + // draw + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool AriannaTheLabrynthServantSummon() + { + // summon for search + if (!CheckWhetherNegated(true, true) && !activatedCardIdList.Contains(Card.Id)) + { + summoned = true; + return true; + } + + // summon for attack + if (Duel.Turn > 1 && Duel.Player == 0 && Duel.Phase < DuelPhase.Main2 && Enemy.GetMonsterCount() == 0 && !Bot.HasInHand(CardId.ArianeTheLabrynthServant)) + { + summoned = true; + return true; + } + + return false; + } + public bool AriannaTheLabrynthServantActivate() + { + if (CheckWhetherNegated()) return false; + // search or draw + // search target is overrided in OnSelectCard() + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool AshBlossomActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC)) return false; + if (DefaultAshBlossomAndJoyousSpring()) + { + if (Util.GetLastChainCard().Location == CardLocation.MonsterZone) currentNegateMonsterList.Add(Util.GetLastChainCard()); + return true; + } + return false; + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); + } + + public bool FurnitureSetWelcomeActivate() + { + if (furnitureActivating && (Card.Location == CardLocation.Hand || !DefaultOnBecomeTarget())) return false; + if (Util.ChainContainsCard(new int[]{ _CardId.DivineArsenalAAZEUS_SkyThunder, _CardId.EvenlyMatched, _CardId.EvilswarmExcitonKnight })) return false; + + if (CheckWhetherNegated()) return false; + if (Card.Location != CardLocation.Grave) + { + bool becomeTarget = Card.Location == CardLocation.MonsterZone && DefaultOnBecomeTarget() && !escapeTargetList.Contains(Card); + bool lackUnimportantCost = Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth)); + if (lackUnimportantCost) + { + List handCost = Bot.Hand.Where(card => card != Card).ToList(); + lackUnimportantCost &= handCost.Count() <= 2 && handCost.All(card => card.IsCode(_CardId.MaxxC, _CardId.AshBlossom)); + } + bool activateFlag = becomeTarget; + // set big welcome for lovely + bool canActivateSetBigWelcomeThisTurn = CheckRemainInDeck(CardId.BigWelcomeLabrynth) > 0 && cooclockAffected && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && (Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle)) + && (!Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth) && !setTrapThisTurn.Contains(card))) + && ( + Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)) + || (Bot.HasInGraveyard(CardId.LabrynthCooclock) && !activatedCardIdList.Contains(CardId.LabrynthCooclock + 1)) + || (Bot.HasInHand(CardId.LadyLabrynthOfTheSilverCastle) && !activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle)) + ); + if (canActivateSetBigWelcomeThisTurn && ShouldSetBigWelcome()) + { + bool force = becomeTarget | GetProblematicEnemyCardList(false).Count() > 0; + ClientCard cost = FurnitureGetCost(force); + if (cost != null) + { + AI.SelectCard(cost); + AI.SelectNextCard(CardId.BigWelcomeLabrynth); + activatedCardIdList.Add(Card.Id); + furnitureActivating = true; + SelectSTPlace(null, true); + return true; + } + } + bool keepOnField = (cooclockActivating || cooclockAffected) && activatedCardIdList.Contains(CardId.LabrynthCooclock + 1) + && Card.Location == CardLocation.MonsterZone && !Bot.GetMonsters().Any(card => card.IsFaceup() && card != Card && card.HasSetcode(SetcodeLabrynth)) + && setTrapThisTurn.Count() > 0; + // normal set + activateFlag |= Duel.Phase > DuelPhase.Main2 && !lackUnimportantCost && !keepOnField; + activateFlag |= Bot.HasInGraveyard(new List { CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth }) + && Bot.HasInHand(CardId.TransactionRollback) && !activatedCardIdList.Contains(CardId.TransactionRollback); + if (Duel.CurrentChain.Any(card => card != null && card.Controller == 0 && card.IsCode(CardId.BigWelcomeLabrynth) && card.Location == CardLocation.SpellZone) + && !(Bot.GetMonsterCount() == 1 && Card.Location == CardLocation.MonsterZone)) + { + activateFlag |= !lackUnimportantCost && Bot.GetMonsters().Any(card => card != Card) && !Bot.HasInGraveyard(Card.Id) && !activatedCardIdList.Contains(Card.Id + 1); + } + // trigger cooclock to defense + activateFlag |= !Util.ChainContainPlayer(0) && Duel.Player == 1 && Bot.UnderAttack && Bot.GetMonsterCount() == 0 + && Bot.HasInGraveyard(CardId.LabrynthCooclock) && !activatedCardIdList.Contains(CardId.LabrynthCooclock + 1) + && !(Bot.HasInHand(CardId.LadyLabrynthOfTheSilverCastle) && !activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle)); + + if (activateFlag) + { + ClientCard cost = FurnitureGetCost(becomeTarget); + if (cost != null) + { + AI.SelectCard(cost); + activatedCardIdList.Add(Card.Id); + furnitureActivating = true; + bool setWelcome = Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth)); + setWelcome |= Bot.GetMonsterCount() == 0 && !Bot.HasInHandOrInSpellZone(CardId.WelcomeLabrynth) + && (!Bot.HasInGraveyard(CardId.LabrynthCooclock) || activatedCardIdList.Contains(CardId.LabrynthCooclock + 1)) + && ((Duel.Player == 0 && Duel.Phase > DuelPhase.Main2) || !Bot.Hand.Any(card => card != Card && card.Level <= 4)); + if (setWelcome) + { + AI.SelectNextCard(CardId.WelcomeLabrynth); + } else { + AI.SelectNextCard(CardId.BigWelcomeLabrynth); + } + SelectSTPlace(null, true); + return true; + } + } + } + + return false; + } + + public ClientCard FurnitureGetCost(bool force = false, List ignoreList = null) + { + if (ignoreList == null) ignoreList = new List(); + // advance cost + List advancedCostIdList = new List{ + CardId.TransactionRollback, CardId.LovelyLabrynthOfTheSilverCastle, CardId.AriasTheLabrynthButler, CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, + CardId.WelcomeLabrynth + }; + foreach (int checkId in advancedCostIdList) + { + ClientCard cost = Bot.Hand.FirstOrDefault(card => !ignoreList.Contains(card) && card.IsCode(checkId) && card != Card); + if (cost != null) return cost; + } + // dump cost + List canCostHand = Bot.Hand.Where(card => !ignoreList.Contains(card)).ToList(); + List appearedCode = new List(canCostHand.Count()); + foreach (ClientCard hand in canCostHand) + { + if (Duel.CurrentChain.Contains(hand)) continue; + if (appearedCode.Contains(hand.Id)) return hand; + appearedCode.Add(hand.Id); + } + List costIdList = new List{ + _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.UnchainedSoulOfSharvara, CardId.EscapeOfTheUnchained, CardId.DestructiveDarumaKarmaCannon, + CardId.LabrynthCooclock, CardId.ArianeTheLabrynthServant, CardId.WelcomeLabrynth, CardId.PotOfExtravagance, CardId.LadyLabrynthOfTheSilverCastle + }; + if (force) costIdList.AddRange(new List{CardId.AriannaTheLabrynthServant, _CardId.AshBlossom, CardId.BigWelcomeLabrynth, _CardId.MaxxC}); + foreach (int checkId in costIdList) + { + ClientCard target = canCostHand.FirstOrDefault(card => !Duel.CurrentChain.Contains(card) && card.IsCode(checkId) && !Duel.CurrentChain.Contains(card)); + if (target != null) return target; + } + foreach (int checkId in costIdList) + { + ClientCard target = canCostHand.FirstOrDefault(card => card.IsCode(checkId) && !Duel.CurrentChain.Contains(card)); + if (target != null) return target; + } + + return null; + } + + public bool ShouldSetBigWelcome(bool checkArianna = true) + { + if (CheckWhetherWillbeRemoved()) return false; + bool shouldTriggerBigWelcomeFlag = GetProblematicEnemyCardList(false).Count() > 0; + shouldTriggerBigWelcomeFlag |= Duel.Player == 1 && Duel.Phase > DuelPhase.Main2; + shouldTriggerBigWelcomeFlag |= Duel.Player == 1 && GetProblematicEnemyCardList(false).Count() == 0 && GetProblematicEnemyMonster(selfType: CardType.Monster) == null + && Enemy.Hand.Count() == 1; + if (checkArianna) shouldTriggerBigWelcomeFlag |= Duel.Player == 0 && !summoned && Bot.HasInHandOrHasInMonstersZone(CardId.AriannaTheLabrynthServant) + && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant); + shouldTriggerBigWelcomeFlag |= Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2; + return shouldTriggerBigWelcomeFlag; + } + + public bool LabrynthCooclockActivate() + { + if (Card.Location == CardLocation.Hand) + { + bool shouldTriggerBigWelcomeFlag = false; + if (Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle)) { + shouldTriggerBigWelcomeFlag |= ShouldSetBigWelcome(); + } + shouldTriggerBigWelcomeFlag &= !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth); + if (shouldTriggerBigWelcomeFlag && !Bot.GetSpells().Any(card => card.IsFacedown() && !setTrapThisTurn.Contains(card) && card.IsCode(CardId.BigWelcomeLabrynth))) + { + // whether have labrynth to trigger cooclock + bool haveBigWelcome = Duel.Player == 0 && Bot.HasInHand(CardId.BigWelcomeLabrynth) + && (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)) || (!summoned && Bot.Hand.Any(card => card != Card && card.HasType(CardType.Monster) && card.Level <= 4 && card.HasSetcode(SetcodeLabrynth)))); + if (CheckRemainInDeck(CardId.BigWelcomeLabrynth) > 0) + { + foreach (int checkId in new List { CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie }) + { + if (activatedCardIdList.Contains(checkId) || CheckCalledbytheGrave(checkId) > 0) continue; + if (Bot.HasInHand(checkId) && Bot.Hand.Count > 2 || Bot.GetMonsters().Any(card => card.IsFaceup() && !card.IsDisabled() && card.IsCode(checkId)) && Bot.Hand.Count > 1) + { + haveBigWelcome = true; + break; + } + } + } + if (haveBigWelcome) + { + activatedCardIdList.Add(Card.Id); + cooclockActivating = true; + return true; + } + } + + bool haveLabrynth = Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)); + bool triggerFlag = Duel.Player == 1 && Duel.Phase <= DuelPhase.Main2 && setTrapThisTurn.Any(card => !activatedCardIdList.Contains(card.Id)) && haveLabrynth; + triggerFlag |= Duel.Player == 1 && activatedCardIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle + 1) + && (Bot.GetSpells().Any(card => card.IsFacedown() && card.Type == (int)CardType.Trap) || Util.ChainContainsCard(CardId.LadyLabrynthOfTheSilverCastle)); + triggerFlag |= setTrapThisTurn.Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)) && haveLabrynth; + triggerFlag |= setTrapThisTurn.Any(card => card.IsFacedown() && card.IsCode(CardId.WelcomeLabrynth) && !activatedCardIdList.Contains(CardId.WelcomeLabrynth)) && haveLabrynth; + + if (triggerFlag) + { + activatedCardIdList.Add(Card.Id); + cooclockActivating = true; + return true; + } + } + + return false; + } + + public bool RecycleActivate() + { + if (Card.Location == CardLocation.Grave) + { + if (Card.IsCode(CardId.LabrynthStovieTorbie, CardId.AriasTheLabrynthButler)) + { + if (CheckShouldNoMoreSpSummon() || GetEmptyMainMonsterZoneCount() + chainSummoningIdList.Count() <= 0) return false; + chainSummoningIdList.Add(Card.Id); + } + if (Card.IsCode(CardId.WelcomeLabrynth)) SelectSTPlace(Card, false); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool ForLinkSummon() + { + if (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained))) return false; + if (Card.Level > 4) return false; + if (CheckShouldNoMoreSpSummon()) return false; + if (!Bot.HasInExtra(CardId.UnchainedSoulLordOfYama)) return false; + + // check whether need summon for material count + List materialList = GetCanBeUsedForLinkMaterial(true, card => !card.HasRace(CardRace.Fiend)); + int materialCount = materialList.Count(); + if (!activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara) && Bot.GetSpells().Any(card => card.IsFacedown()) + && (!activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama) || Bot.HasInHand(CardId.UnchainedSoulOfSharvara))) materialCount++; + if (materialCount != 2) + { + if (materialCount != 1 || !materialList.Any(card => card.IsCode(CardId.UnchainedSoulLordOfYama))) return false; + } + + if (!Bot.HasInExtra(CardId.UnchainedSoulLordOfYama) && !materialList.Any(card => card.HasSetcode(SetcodeUnchained))) return false; + bool needSummon = false; + needSummon |= Enemy.GetMonsterCount() > 0 && Bot.HasInExtra(CardId.UnchainedSoulOfAnguish); + needSummon |= Bot.HasInExtra(CardId.UnchainedSoulOfRage); + if (needSummon) + { + // use monster with least attack + YGOSharp.OCGWrapper.NamedCard thisCardData = YGOSharp.OCGWrapper.NamedCard.Get(Card.Id); + if (thisCardData == null) return false; + if (thisCardData.Race != (int)CardRace.Fiend) return false; + foreach (ClientCard hand in Bot.Hand) + { + YGOSharp.OCGWrapper.NamedCard compareCardData = YGOSharp.OCGWrapper.NamedCard.Get(hand.Id); + if (compareCardData == null) continue; + if (!compareCardData.HasType(CardType.Monster) || compareCardData.Level > 4) continue; + if (compareCardData.Attack < thisCardData.Attack) return false; + } + summoned = true; + return true; + } + + return false; + } + public bool ForSynchroSummon() + { + if (Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained))) return false; + if (!Card.IsCode(new List { CardId.LabrynthStovieTorbie, CardId.ArianeTheLabrynthServant, CardId.AriannaTheLabrynthServant })) return false; + if (CheckShouldNoMoreSpSummon()) return false; + if (!Bot.HasInExtra(CardId.ChaosAngel) || dimensionalBarrierAnnouced.Contains(HintMsg.SYNCHRO)) return false; + + bool checkFlag = GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0 && !CheckWhetherNegated(true, true, CardType.Monster); + if (Card.IsCode(CardId.LabrynthStovieTorbie)) + { + if (!Bot.GetMonsters().Any(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link) + && card.Level == 8 && card.HasAttribute(CardAttribute.Light | CardAttribute.Dark))) return false; + summoned = true; + return true; + } + else + { + if (!Bot.GetMonsters().Any(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link) + && card.Level == 6 && card.HasAttribute(CardAttribute.Light | CardAttribute.Dark))) return false; + summoned = true; + return true; + } + } + public bool ForAnimaSummon() + { + if (banSpSummonExceptFiendCount > 0 || !Bot.HasInExtra(CardId.RelinquishedAnima)) return false; + if (CheckWhetherNegated() || Duel.Turn == 1) return false; + + bool checkFlag = Bot.MonsterZone[1] == null && Enemy.MonsterZone[6] != null && Enemy.MonsterZone[6].HasType(CardType.Link) && Enemy.MonsterZone[6].HasLinkMarker(CardLinkMarker.Top); + checkFlag |= Bot.MonsterZone[3] == null && Enemy.MonsterZone[5] != null && Enemy.MonsterZone[5].HasType(CardType.Link) && Enemy.MonsterZone[5].HasLinkMarker(CardLinkMarker.Top); + if (Bot.GetMonstersExtraZoneCount() == 0) checkFlag |= Enemy.MonsterZone[1] != null || Enemy.MonsterZone[3] != null; + + return checkFlag; + } + + public bool LabrynthForCooClockSummon() + { + if (!cooclockAffected) return false; + if (Card.Level > 4 || !Card.HasSetcode(SetcodeLabrynth)) return false; + bool welcomeFlag = Bot.Hand.Any(card => (card.IsCode(CardId.WelcomeLabrynth) && !activatedCardIdList.Contains(CardId.WelcomeLabrynth)) + || (card.IsCode(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth))); + welcomeFlag |= Bot.GetSpells().Any(card => card.IsFacedown() && setTrapThisTurn.Contains(card) && ( + (card.IsCode(CardId.WelcomeLabrynth) && !activatedCardIdList.Contains(CardId.WelcomeLabrynth)) + || (card.IsCode(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)))); + if (welcomeFlag && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth))) + { + // summon highest attack + int currentAttack = 0; + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(Card.Id); + if (cardData != null) currentAttack = cardData.Attack; + List summonList = Bot.Hand.Where(card => card.IsMonster() && card.Level <= 4 && card.HasSetcode(SetcodeLabrynth)).ToList(); + foreach (ClientCard checkCard in summonList) + { + cardData = YGOSharp.OCGWrapper.NamedCard.Get(checkCard.Id); + if (cardData != null && cardData.Attack < currentAttack) return false; + } + + return true; + } + return false; + } + + public bool ForBigWelcomeSummon() + { + if (Bot.HasInSpellZone(CardId.BigWelcomeLabrynth) && Bot.GetMonsterCount() == 0 && Card.Level <= 4) + { + summoned = true; + return true; + } + return false; + } + + public bool PotOfExtravaganceActivate() + { + if (CheckWhetherNegated()) return false; + SelectSTPlace(Card, true); + activatedCardIdList.Add(Card.Id); + AI.SelectOption(1); + return true; + } + + public bool WelcomeLabrynthActivate() + { + return WelcomeLabrynthActivateCheck(false); + } + public bool WelcomeLabrynthActivateCopy() + { + return WelcomeLabrynthActivateCheck(true); + } + public bool WelcomeLabrynthSetCheck() + { + return !CheckShouldNoMoreSpSummon() && WelcomeLabrynthActivateCheck(true, true); + } + public bool WelcomeLabrynthActivateCheck(bool onlyCheck = false, bool noSelect = false) + { + if (Card.Location == CardLocation.SpellZone || onlyCheck) + { + if (GetEmptyMainMonsterZoneCount() == 0) return false; + if (CheckShouldNoMoreSpSummon()) return false; + bool activateTimingFlag = Duel.Phase > DuelPhase.Main2 || (Card.IsCode(CardId.AriasTheLabrynthButler) && (CurrentTiming & hintTimingMainEnd) > 0); + + bool becomeTarget = Card.Location == CardLocation.SpellZone && DefaultOnBecomeTarget(); + if ((Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 || Duel.Player == 1 && activateTimingFlag) + && CheckRemainInDeck(CardId.ArianeTheLabrynthServant) > 0 && Bot.HasInHandOrInSpellZone(CardId.TransactionRollback) + && !chainSummoningIdList.Contains(CardId.ArianeTheLabrynthServant)) + { + if (!noSelect) + { + chainSummoningIdList.Add(CardId.ArianeTheLabrynthServant); + activatedCardIdList.Add(Card.Id); + } + return true; + } + bool ariannaCheck = !Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) + || !(Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle)); + ariannaCheck |= Duel.Player == 1 && activateTimingFlag; + ariannaCheck |= Duel.Player == 0; + if (ariannaCheck) + { + if (CheckRemainInDeck(CardId.AriannaTheLabrynthServant) > 0 && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) + && !CheckWhetherNegated(true, true, CardType.Monster) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + if (!noSelect) + { + chainSummoningIdList.Add(CardId.AriannaTheLabrynthServant); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + if (Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth)) + { + if (Bot.HasInHand(CardId.LovelyLabrynthOfTheSilverCastle) && CheckRemainInDeck(CardId.AriasTheLabrynthButler) > 0 + && !chainSummoningIdList.Contains(CardId.AriasTheLabrynthButler) && !Bot.HasInMonstersZone(CardId.AriasTheLabrynthButler, true, false, true)) + { + if (!noSelect) + { + chainSummoningIdList.Add(CardId.AriasTheLabrynthButler); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + + bool activateFlag = becomeTarget; + activateFlag |= Bot.UnderAttack && Bot.GetMonsterCount() == 0; + activateFlag |= ShouldSetBigWelcome(false); + if (activateFlag) + { + if (!noSelect) + { + if (Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && CheckRemainInDeck(CardId.LovelyLabrynthOfTheSilverCastle) > 0 && !chainSummoningIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle)) + { + chainSummoningIdList.Add(CardId.LovelyLabrynthOfTheSilverCastle); + } + else if (!activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && CheckRemainInDeck(CardId.AriannaTheLabrynthServant) > 0 + && !CheckWhetherNegated(true, true, CardType.Monster) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + chainSummoningIdList.Add(CardId.AriannaTheLabrynthServant); + } + else if (Bot.HasInGraveyard(CardId.BigWelcomeLabrynth) && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) + && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasRace(CardRace.Fiend) && card.Level >= 8) + && CheckRemainInDeck(CardId.LadyLabrynthOfTheSilverCastle) > 0 && !chainSummoningIdList.Contains(CardId.LadyLabrynthOfTheSilverCastle)) + { + chainSummoningIdList.Add(CardId.LadyLabrynthOfTheSilverCastle); + } + else { + int selectId = 0; + List checkIdList = new List{CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock}; + foreach (int checkId in checkIdList) + { + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(checkId) && CheckRemainInDeck(checkId) > 0 + && !chainSummoningIdList.Contains(checkId)) + { + selectId = checkId; + break; + } + } + List fullCheckIdList = new List{ + CardId.LadyLabrynthOfTheSilverCastle, CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock, + CardId.AriasTheLabrynthButler, CardId.ArianeTheLabrynthServant, CardId.AriannaTheLabrynthServant + }; + if (selectId == 0) + { + foreach (int checkId in fullCheckIdList) + { + if (CheckRemainInDeck(checkId) > 0 && !chainSummoningIdList.Contains(checkId)) + { + selectId = checkId; + break; + } + } + } + if (selectId > 0) { + chainSummoningIdList.Add(selectId); + } + } + ResetCooclockEffect(onlyCheck); + + activatedCardIdList.Add(Card.Id); + } + return true; + } + + } + + return false; + } + + public bool TransactionRollbackActivate() + { + if (Card.Location == CardLocation.Grave) + { + SortedList> checkList = new SortedList> { + {CardId.BigWelcomeLabrynth, BigWelcomeLabrynthActivateCopy}, + {_CardId.DimensionalBarrier, DimensionalBarrierActivate}, + {CardId.EscapeOfTheUnchained, EscapeOfTheUnchainedActivateCopy}, + {_CardId.InfiniteImpermanence, InfiniteImpermanenceActivateCopy}, + {CardId.WelcomeLabrynth, WelcomeLabrynthActivateCopy}, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonActivate} + }; + foreach (KeyValuePair> pair in checkList) + { + if (Bot.HasInGraveyard(pair.Key) && pair.Value()) + { + rollbackCopyCardId = pair.Key; + AI.SelectCard(pair.Key); + return true; + } + } + } + if (Card.Location == CardLocation.SpellZone) + { + if (CheckWhetherNegated()) return false; + SortedList> checkList = new SortedList> { + {CardId.WelcomeLabrynth, WelcomeLabrynthActivateCopy}, + {_CardId.CompulsoryEvacuationDevice, DefaultCompulsoryEvacuationDevice }, + {CardId.DestructiveDarumaKarmaCannon, DestructiveDarumaKarmaCannonActivate}, + {_CardId.DimensionalBarrier, DimensionalBarrierActivate}, + {CardId.EscapeOfTheUnchained, EscapeOfTheUnchainedActivateCopy}, + {_CardId.InfiniteImpermanence, InfiniteImpermanenceActivateCopy}, + {_CardId.BreakthroughSkill, DefaultBreakthroughSkill}, + {CardId.BigWelcomeLabrynth, BigWelcomeLabrynthActivateCopy} + }; + foreach (KeyValuePair> pair in checkList) + { + if (Enemy.HasInGraveyard(pair.Key) && pair.Value()) + { + rollbackCopyCardId = pair.Key; + AI.SelectCard(pair.Key); + ResetCooclockEffect(false); + return true; + } + } + } + return false; + } + + public bool InfiniteImpermanenceActivate() + { + return InfiniteImpermanenceActivateCheck(false); + } + public bool InfiniteImpermanenceActivateCopy() + { + return InfiniteImpermanenceActivateCheck(true); + } + public bool InfiniteImpermanenceSetCheck() + { + return InfiniteImpermanenceActivateCheck(true, true); + } + public bool InfiniteImpermanenceActivateCheck(bool onlyCheck = false, bool noSelect = false) + { + if (CheckWhetherNegated()) return false; + + ClientCard LastChainCard = Util.GetLastChainCard(); + + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int thisSeq = -1; + int thatSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) thisSeq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) thatSeq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) thatSeq = i; + } + if ( (thisSeq * thatSeq >= 0 && thisSeq + thatSeq == 4) + || Util.IsChainTarget(Card) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true, selfType: CardType.Trap); + if (!noSelect) + { + if (target != null) + { + AI.SelectCard(target); + } else { + AI.SelectCard(Enemy.GetMonsters()); + } + } + if (!onlyCheck) + { + infiniteImpermanenceList.Add(thatSeq); + if (cooclockAffected && setTrapThisTurn.Contains(Card)) + { + cooclockAffected = false; + setTrapThisTurn.Remove(Card); + } + } + return true; + } + } + + // negate monster + List shouldNegateList = GetMonsterListForTargetNegate(true, CardType.Trap); + if (shouldNegateList.Count() > 0) + { + ClientCard negateTarget = shouldNegateList[0]; + currentNegateMonsterList.Add(negateTarget); + + if (Card.Location == CardLocation.SpellZone && !onlyCheck) + { + for (int i = 0; i < 5; ++ i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + if (!noSelect) AI.SelectCard(negateTarget); + currentDestroyCardList.Add(negateTarget); + ResetCooclockEffect(onlyCheck); + return true; + } + + return false; + } + + public bool DestructiveDarumaKarmaCannonActivate() + { + return DestructiveDarumaKarmaCannonActivateCheck(false); + } + public bool DestructiveDarumaKarmaCannonSetCheck() + { + return DestructiveDarumaKarmaCannonActivateCheck(true); + } + public bool DestructiveDarumaKarmaCannonActivateCheck(bool noSelect = false) + { + bool becomeTarget = Card.Location == CardLocation.SpellZone && DefaultOnBecomeTarget(); + bool activateFlag = becomeTarget && Util.IsOneEnemyBetter(true); + bool canTriggerLovely = + (!activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && Bot.GetSpells().Any(card => card.IsFacedown() && card.IsCode(CardId.BigWelcomeLabrynth) && (!cooclockAffected || !setTrapThisTurn.Contains(card))) + || Util.ChainContainsCard(CardId.BigWelcomeLabrynth)) + && (Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || (CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle) && Bot.GetMonsterCount() > 0)) + && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + + activateFlag |= Bot.UnderAttack && (Bot.BattlingMonster?.GetDefensePower() ?? 0) <= (Enemy.BattlingMonster?.GetDefensePower() ?? 0) && !Util.ChainContainPlayer(0) && !canTriggerLovely; + activateFlag |= Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2 && Bot.GetMonsterCount() == 0 && Enemy.GetMonsterCount() > 0; + activateFlag |= Enemy.HasInMonstersZone(CardId.AccesscodeTalker, true) && !Util.ChainContainPlayer(0); + int linkCount = 0; + foreach (ClientCard monster in Enemy.GetMonsters()) + { + if (monster.IsFacedown()) continue; + if (!monster.HasType(CardType.Link)) linkCount++; + else linkCount += monster.LinkCount; + } + activateFlag |= linkCount >= 6 && Util.IsOneEnemyBetter(true); + if (activateFlag) + { + if (!noSelect) + { + currentDestroyCardList.AddRange(Enemy.GetMonsters()); + escapeTargetList.AddRange(Bot.GetMonsters()); + } + return true; + } + + return false; + } + + public bool EscapeOfTheUnchainedActivate() + { + return EscapeOfTheUnchainedActivateCheck(false); + } + public bool EscapeOfTheUnchainedActivateCopy() + { + return EscapeOfTheUnchainedActivateCheck(true); + } + public bool EscapeOfTheUnchainedActivateCheck(bool onlyCheck = false, bool noSelect = false) + { + if (Card.Location == CardLocation.SpellZone || onlyCheck) + { + // select targeted unchained + ClientCard selfTarget = Bot.GetMonsters().FirstOrDefault(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained) + && Duel.ChainTargets.Contains(card) && !escapeTargetList.Contains(card)); + if (selfTarget == null) + { + selfTarget = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained)) + .OrderBy(card => card.Attack).FirstOrDefault(); + } + if (selfTarget == null) return false; + // destroy danger card + List dangerList = GetProblematicEnemyCardList(true, selfType: CardType.Trap); + if (dangerList.Count() > 0 && Duel.LastChainPlayer != 0) + { + if (!noSelect) + { + AI.SelectCard(selfTarget); + AI.SelectNextCard(dangerList); + escapeTargetList.Add(selfTarget); + currentDestroyCardList.Add(dangerList[0]); + activatedCardIdList.Add(Card.Id); + } + return true; + } + + // best monster + int botBestPower = Util.GetBestPower(Bot); + if (Duel.Player == 1 && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + List dangerMonsters = Enemy.GetMonsters().Where(card => card.IsFaceup() && card.Attack >= botBestPower + && !currentDestroyCardList.Contains(card) && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + .OrderByDescending(card => card.Attack).ToList(); + if (dangerMonsters.Count() > 0) + { + if (!noSelect) + { + AI.SelectCard(selfTarget); + AI.SelectNextCard(dangerMonsters); + escapeTargetList.Add(selfTarget); + currentDestroyCardList.Add(dangerMonsters[0]); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + + // end phase + bool activateFlag = Duel.Player == 1 && Duel.Phase > DuelPhase.Main2 + && ((Bot.HasInGraveyard(CardId.UnchainedSoulLordOfYama) && !activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama + 1)) + || (Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1))); + activateFlag |= DefaultOnBecomeTarget() && Card.Location == CardLocation.SpellZone && !Util.ChainContainsCard(_CardId.EvenlyMatched); + if (activateFlag) + { + List destroyTarget = GetNormalEnemyTargetList(true, true, CardType.Trap); + if (destroyTarget.Count() > 0) + { + if (!noSelect) + { + AI.SelectCard(selfTarget); + AI.SelectNextCard(destroyTarget); + escapeTargetList.Add(selfTarget); + currentDestroyCardList.Add(destroyTarget[0]); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + + } else { + if (!noSelect) + { + AI.SelectCard(CardId.UnchainedSoulOfSharvara); + activatedCardIdList.Add(Card.Id + 1); + } + return true; + } + + return false; + } + + public bool DimensionalBarrierActivate() + { + if (Duel.Player == 0 && Duel.Turn == 1) return false; + if (CheckWhetherNegated()) return false; + Dictionary> checkDict = new Dictionary> + { + {HintMsg.RITUAL, DimensionalBarrierForRitual}, + {HintMsg.FUSION, DimensionalBarrierForFusion}, + {HintMsg.SYNCHRO, DimensionalBarrierForSynchro}, + {HintMsg.XYZ, DimensionalBarrierForXyz}, + {HintMsg.PENDULUM, DimensionalBarrierForPendulum}, + }; + foreach (KeyValuePair> checkType in checkDict) + { + if (dimensionalBarrierAnnouced.Contains(checkType.Key)) continue; + if (checkType.Value()) { + ResetCooclockEffect(false); + return true; + } + } + + return DefaultOnBecomeTarget(); + } + public bool DimensionalBarrierForRitual() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() && chainCard.HasType(CardType.Ritual) + && (chainCard.HasType(CardType.Spell) || (chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)))) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + + return false; + } + public bool DimensionalBarrierForFusion() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() && (chainCard.IsFusionSpell() + || (chainCard.HasType(CardType.Fusion) && chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)))) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + + return false; + } + public bool DimensionalBarrierForSynchro() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() + && chainCard.HasType(CardType.Synchro) && chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + if (Duel.Player == 1 && !Util.ChainContainsCard(CardId.DestructiveDarumaKarmaCannon) && Enemy.ExtraDeck.Count() > 0) + { + bool tunerCheck = false; + bool nontunerCheck = false; + foreach (ClientCard monster in Enemy.GetMonsters()) + { + if (monster.IsFacedown() || monster.HasType(CardType.Xyz | CardType.Link)) continue; + if (monster.HasType(CardType.Tuner)) tunerCheck = true; + else nontunerCheck = true; + } + if (tunerCheck && nontunerCheck) return true; + } + + return false; + } + public bool DimensionalBarrierForXyz() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() + && chainCard.HasType(CardType.Xyz) && chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + if (Duel.Player == 1 && !Util.ChainContainsCard(CardId.DestructiveDarumaKarmaCannon) && Enemy.ExtraDeck.Count() > 0) + { + List existsLevel = new List(6); + foreach (ClientCard monster in Enemy.GetMonsters()) + { + if (monster.IsFacedown()) continue; + if (monster.IsOneForXyz()) return true; + if (monster.HasType(CardType.Xyz | CardType.Token)) continue; + int level = monster.Level; + if (level != 2 && monster.HasType(CardType.Link)) continue; + if (existsLevel.Contains(level)) return true; + existsLevel.Add(level); + } + } + + return false; + } + public bool DimensionalBarrierForPendulum() + { + foreach (ClientCard chainCard in Duel.CurrentChain) + { + if (chainCard != null && chainCard.Controller == 1 && !chainCard.IsDisabled() + && chainCard.HasType(CardType.Pendulum) && chainCard.Location == CardLocation.MonsterZone && !currentNegateMonsterList.Contains(chainCard)) + { + if (dimensionBarrierAnnouncing) currentNegateMonsterList.Add(chainCard); + return true; + } + } + + ClientCard l = Enemy.SpellZone[6]; + ClientCard r = Enemy.SpellZone[7]; + if (l != null && r != null && l.LScale != r.RScale) return true; + + return false; + } + + public bool BigWelcomeLabrynthActivate() + { + return BigWelcomeLabrynthActivateCheck(false); + } + public bool BigWelcomeLabrynthBecomeTargetActivate() + { + if (DefaultOnBecomeTarget()) return BigWelcomeLabrynthActivateCheck(false); + return false; + } + public bool BigWelcomeLabrynthActivateCopy() + { + return BigWelcomeLabrynthActivateCheck(true); + } + public bool BigWelcomeLabrynthSetCheck() + { + return !CheckShouldNoMoreSpSummon() && BigWelcomeLabrynthActivateCheck(true, true); + } + public bool BigWelcomeLabrynthActivateCheck(bool onlyCheck = false, bool noSelect = false) + { + if (CheckWhetherNegated()) return false; + if (Card.Location != CardLocation.SpellZone && !onlyCheck) return false; + if (GetEmptyMainMonsterZoneCount() == 0) return false; + bool activateTimingFlag = Duel.Phase > DuelPhase.Main2 || (Card.IsCode(CardId.AriasTheLabrynthButler) && (CurrentTiming & hintTimingMainEnd) > 0); + + bool needDestroyFlag = GetProblematicEnemyCardList(false).Count() > 0; + needDestroyFlag |= activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && activateTimingFlag; + needDestroyFlag |= Bot.UnderAttack && (Bot.BattlingMonster?.GetDefensePower() ?? 0) <= (Enemy.BattlingMonster?.GetDefensePower() ?? 0) && Duel.LastChainPlayer != 0; + needDestroyFlag |= Duel.Turn == 1 && Duel.Player == 0 && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + needDestroyFlag |= Duel.Turn == 1 && Enemy.GetMonsterCount() == 0 && Enemy.GetSpellCount() == 0 && Enemy.Hand.Count > 0 + && (CurrentTiming & hintTimingMainEnd) > 0; + + // do not activate when welcome is activating + bool haveEnemyChain = false; + bool haveWelcome = false; + foreach (ClientCard chain in Duel.CurrentChain) + { + if (chain != null) + { + if (chain.Controller == 1) + { + haveEnemyChain = true; + break; + } + if (chain.IsCode(CardId.WelcomeLabrynth, CardId.TransactionRollback, CardId.LadyLabrynthOfTheSilverCastle)) haveWelcome = true; + } + } + if (haveWelcome && !haveEnemyChain) return false; + + // escape target + List targetList = Bot.GetMonsters(); + foreach (ClientCard target in targetList) + { + if (Duel.ChainTargets.Contains(target) && !escapeTargetList.Contains(target) + && !(target.IsCode(CardId.UnchainedSoulOfRage, CardId.UnchainedSoulOfAnguish) && Duel.CurrentChain.Contains(target))) + { + Logger.DebugWriteLine("[BigWelcome]escape target"); + if (!noSelect) + { + bigwelcomeEscaseTarget = target; + escapeTargetList.Add(target); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + + if (Bot.GetMonsterCount() > 0) + { + bool flag1 = needDestroyFlag && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1) && (Util.ChainContainPlayer(1) || Duel.LastChainPlayer != 0); + bool flag2 = DefaultOnBecomeTarget(); + bool flag3 = Duel.Player == 1 && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && activateTimingFlag; + bool flag4 = Duel.Player == 0 && Duel.LastChainPlayer != 0 && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth); + Logger.DebugWriteLine("[BigWelcome count>0]flag: "+ flag1 + " " + flag2 + " " + flag3 + " " + flag4); + needDestroyFlag |= flag3; + if (flag1 || flag2 || flag3 || flag4) + { + bool spSummonLovely = CheckBigWelcomeCanSpSummon(CardId.LovelyLabrynthOfTheSilverCastle) && !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1); + bool haveLovely = Bot.GetMonsters().Any(card => card.IsFaceup() && card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)); + if (!noSelect) + { + activatedCardIdList.Add(Card.Id); + } + ResetCooclockEffect(onlyCheck); + return true; + } + } + else { + bool activateFlag = DefaultOnBecomeTarget(); + activateFlag |= Duel.Player == 1 && !activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && activateTimingFlag; + activateFlag |= Duel.Player == 0 && !summoned && !Bot.HasInHand(CardId.AriannaTheLabrynthServant) && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) + && !(Duel.Phase < DuelPhase.Main1 && Bot.HasInHand(CardId.PotOfExtravagance) && Bot.ExtraDeck.Count() >= 3) + && !(Duel.CurrentChain.Any(card => card.IsCode(CardId.PotOfExtravagance) && card.Controller == 0)); + if (activateFlag && !noSelect) + { + activatedCardIdList.Add(Card.Id); + ResetCooclockEffect(onlyCheck); + return true; + } + } + + return false; + } + + public bool BigWelcomeLabrynthGraveActivate() + { + if (Card.Location == CardLocation.Grave) + { + // bounce enemy + if (Bot.GetMonsters().Any(card => card.Level >= 8 && card.IsFaceup() && card.HasRace(CardRace.Fiend) && !card.HasType(CardType.Xyz | CardType.Link))) + { + // danger monster + ClientCard problemCard = GetProblematicEnemyMonster(-1, true, true, CardType.Trap); + if (problemCard != null) + { + AI.SelectCard(problemCard); + currentDestroyCardList.Add(problemCard); + activatedCardIdList.Add(Card.Id); + return true; + } + // problem spell + if (!Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) || + activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle) && activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle + 1)) + { + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && c.IsFaceup() + && c.IsFloodgate() + && !c.IsShouldNotBeTarget() && (c.HasType(CardType.Trap) || Duel.Player == 0)).ToList(); + + problemEnemySpellList.AddRange(Enemy.SpellZone.Where(c => c?.Data != null && c.IsFaceup() && !problemEnemySpellList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) + && !c.IsShouldNotBeTarget() && (c.HasType(CardType.Trap) || Duel.Player == 0)).ToList()); + + if (problemEnemySpellList.Count() > 0) + { + AI.SelectCard(problemEnemySpellList); + currentDestroyCardList.Add(problemEnemySpellList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + // best monster + int botBestPower = Util.GetBestPower(Bot); + if (Duel.Player == 1 && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + List dangerMonsters = Enemy.GetMonsters().Where(card => card.IsFaceup() && card.Attack >= botBestPower + && !currentDestroyCardList.Contains(card) && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + .OrderByDescending(card => card.Attack).ToList(); + if (dangerMonsters.Count() > 0) + { + AI.SelectCard(dangerMonsters); + currentDestroyCardList.Add(dangerMonsters[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + // end phase + if (Duel.Phase > DuelPhase.Main2) + { + List returnList = GetNormalEnemyTargetList(true, true, CardType.Trap); + if (returnList.Count() > 0) + { + AI.SelectCard(returnList); + currentDestroyCardList.Add(returnList[0]); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + // escape target + List targetList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasRace(CardRace.Fiend)).ToList(); + foreach (ClientCard target in targetList) + { + if (Duel.ChainTargets.Contains(target) && !escapeTargetList.Contains(target) + && !(target.IsCode(CardId.UnchainedSoulOfRage, CardId.UnchainedSoulOfAnguish) && Duel.CurrentChain.Contains(target))) + { + AI.SelectCard(target); + escapeTargetList.Add(target); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + // bounce arianna + if (Duel.Player == 0 && Duel.Phase <= DuelPhase.Main2 && !summoned && !Bot.HasInHand(CardId.AriannaTheLabrynthServant) + && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant) && !chainSummoningIdList.Contains(CardId.AriannaTheLabrynthServant)) + { + ClientCard target = targetList.FirstOrDefault(card => card.IsCode(CardId.AriannaTheLabrynthServant)); + if (target != null) + { + AI.SelectCard(target); + escapeTargetList.Add(target); + activatedCardIdList.Add(Card.Id); + return true; + } + } + + // trigger furniture/welcome + List checkFurnitureList = new List(Bot.Hand); + checkFurnitureList.AddRange(Bot.GetMonsters()); + if ((CheckRemainInDeck(CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth) == 0 + || !checkFurnitureList.Any(card => card.IsCode(CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie))) + && Duel.LastChainPlayer < 0 && Duel.Player == 0 && !Bot.HasInMonstersZone(CardId.LovelyLabrynthOfTheSilverCastle, true, false, true) + && !(cooclockAffected && Bot.HasInHandOrInSpellZone(CardId.BigWelcomeLabrynth))) + { + int checkCount = 0; + List checkIdList = new List { CardId.LabrynthChandraglier, CardId.LabrynthStovieTorbie, CardId.WelcomeLabrynth }; + foreach (int checkId in checkIdList) + { + if (Bot.HasInGraveyard(checkId) && !activatedCardIdList.Contains(checkId + 1)) checkCount++; + } + if (checkCount > 0) + { + ClientCard target = targetList.FirstOrDefault(card => card.IsFaceup() && card.HasRace(CardRace.Fiend) && + ((card.Level <= 4 && !card.HasType(CardType.Link | CardType.Xyz | CardType.Synchro)) || card.IsCode(CardId.LadyLabrynthOfTheSilverCastle))); + if (target != null) + { + AI.SelectCard(target); + escapeTargetList.Add(target); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + } + + return false; + } + + public bool ChaosAngelSpSummonWith2Monster() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + + List level2MonsterList = new List(); + List level4MonsterList = new List(); + List level6MonsterList = new List(); + List level8MonsterList = new List(); + foreach (ClientCard monster in Bot.GetMonsters()) + { + if (monster.IsFaceup() && !monster.HasType(CardType.Xyz | CardType.Link) && monster.HasAttribute(CardAttribute.Light | CardAttribute.Dark)) + { + if (monster.Level == 2) level2MonsterList.Add(monster); + if (monster.Level == 4) level4MonsterList.Add(monster); + if (monster.Level == 6) level6MonsterList.Add(monster); + if (monster.Level == 8) level8MonsterList.Add(monster); + } + } + level2MonsterList.Sort(CompareUsableAttack); + level4MonsterList.Sort(CompareUsableAttack); + level6MonsterList.Sort(CompareUsableAttack); + level8MonsterList.Sort(CompareUsableAttack); + bool checkFlag = GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0 && !CheckWhetherNegated(true, true, CardType.Monster); + ClientCard BestEnemyMonster = Util.GetBestEnemyMonster(); + if (BestEnemyMonster != null && Util.GetBestPower(Bot, true) <= Util.GetBestPower(Enemy)) + { + checkFlag |= Util.GetBestPower(Enemy) <= 3500; + checkFlag |= !BestEnemyMonster.IsShouldNotBeTarget() && !BestEnemyMonster.IsShouldNotBeMonsterTarget(); + } + // 4+6 + if (level4MonsterList.Count() > 0 && level6MonsterList.Count() > 0) + { + List materials = new List{level4MonsterList[0], level6MonsterList[0]}; + bool summonFlag = checkFlag; + if (Enemy.GetMonsterCount() == 0 && Duel.Phase < DuelPhase.Main2) + summonFlag |= GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(materials) + 3500 >= Enemy.LifePoints; + if (summonFlag) + { + AI.SelectMaterials(materials); + return true; + } + } + // 2+8 + if (level2MonsterList.Count() > 0 && level8MonsterList.Count() > 0) + { + foreach (ClientCard level2 in level2MonsterList) + { + foreach (ClientCard level8 in level8MonsterList) + { + List materials = new List{level2, level8}; + if (checkFlag && !(level8.IsCode(CardId.LovelyLabrynthOfTheSilverCastle) && !level8.IsDisabled() && Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth))) + { + AI.SelectMaterials(materials); + return true; + } + if (Enemy.GetMonsterCount() == 0 && GetMaterialAttack(materials) < 3500 && Duel.Phase < DuelPhase.Main2) + { + if (GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(materials) + 3500 >= Enemy.LifePoints) + { + AI.SelectMaterials(materials); + return true; + } + } + } + } + } + return false; + } + public bool ChaosAngelSpSummonWith3Monster() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + + List level2MonsterList = new List(); + List level4MonsterList = new List(); + foreach (ClientCard monster in Bot.GetMonsters()) + { + if (monster.IsFaceup() && !monster.HasType(CardType.Xyz | CardType.Link) && monster.HasAttribute(CardAttribute.Light | CardAttribute.Dark)) + { + if (monster.Level == 2) level2MonsterList.Add(monster); + if (monster.Level == 4) level4MonsterList.Add(monster); + } + } + level2MonsterList.Sort(CompareUsableAttack); + level4MonsterList.Sort(CompareUsableAttack); + bool checkFlag = GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0 && !CheckWhetherNegated(true, true, CardType.Monster); + ClientCard BestEnemyMonster = Util.GetBestEnemyMonster(); + if (BestEnemyMonster != null && Util.GetBestPower(Bot, true) <= Util.GetBestPower(Enemy)) + { + checkFlag |= Util.GetBestPower(Enemy) <= 3500; + checkFlag |= !BestEnemyMonster.IsShouldNotBeTarget() && !BestEnemyMonster.IsShouldNotBeMonsterTarget(); + } + // 2+4+4 + if (level2MonsterList.Count() >= 1 && level4MonsterList.Count() >= 2) + { + foreach (ClientCard level2 in level2MonsterList) + { + for (int level4Index1 = 0; level4Index1 < level4MonsterList.Count() - 1; ++level4Index1) + { + ClientCard level41 = level4MonsterList[level4Index1]; + for (int level4Index2 = level4Index1 + 1; level4Index2 < level4MonsterList.Count(); ++level4Index2) + { + ClientCard level42 = level4MonsterList[level4Index2]; + List materials = new List { level2, level41, level42 }; + bool summonFlag = checkFlag; + if (Enemy.GetMonsterCount() == 0 && Duel.Phase < DuelPhase.Main2) + summonFlag |= GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(materials) + 3500 >= Enemy.LifePoints; + if (summonFlag) + { + AI.SelectMaterials(materials); + return true; + } + } + } + } + } + return false; + } + public bool ChaosAngelActivate() + { + List targetList = GetNormalEnemyTargetList(true, true, CardType.Monster); + if (targetList.Count() > 0) + { + AI.SelectCard(targetList); + currentDestroyCardList.Add(targetList[0]); + return true; + } + + return false; + } + + public bool SummonForTYPHONCheck() + { + if (!Bot.HasInExtra(CardId.SuperStarslayerTYPHON) || Bot.GetMonsters().Any(card => card.IsFaceup())) return false; + if (enemySpSummonFromExLastTurn < 2 && enemySpSummonFromExThisTurn < 2) return false; + if (Card.Level > 4) return false; + + int currentAttack = 0; + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(Card.Id); + if (cardData != null) currentAttack = cardData.Attack; + List summonList = Bot.Hand.Where(card => card.IsMonster() && card.Level <= 4).ToList(); + foreach (ClientCard checkCard in summonList) + { + cardData = YGOSharp.OCGWrapper.NamedCard.Get(checkCard.Id); + if (cardData != null && cardData.Attack < currentAttack) return false; + } + + return true; + } + public bool SuperStarslayerTYPHONSpSummon() + { + ClientCard material = Bot.GetMonsters().Where(card => card.IsFaceup()).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (material == null || (material.Attack >= 2900 && material.Owner == 0)) return false; + + bool checkFlag = GetProblematicEnemyMonster(material.Attack) != null; + checkFlag |= material.Level <= 4; + checkFlag &= !(material.HasType(CardType.Link) && Duel.Phase >= DuelPhase.Main2); + if (checkFlag) + { + Logger.DebugWriteLine("*** TYPHON select: " + material.Name ?? "UnkonwCard"); + AI.SelectMaterials(material); + return true; + } + + return false; + } + public bool SuperStarslayerTYPHONActivate() + { + if (CheckWhetherNegated()) return false; + List targetList = new List(); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsFloodgate() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsMonsterDangerous() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.IsMonsterInvincible() && c.IsFaceup()).OrderByDescending(card => card.Attack)); + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.GetDefensePower() >= Util.GetBestAttack(Bot) && c.IsAttack()).OrderByDescending(card => card.Attack)); + if (Duel.Phase >= DuelPhase.Main2) + targetList.AddRange(Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c) && + c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link | CardType.SpSummon)).OrderByDescending(card => card.Attack)); + + if (targetList.Count() > 0) + { + targetList.AddRange(Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card)).OrderByDescending(card => card.Attack)); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() && !targetList.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Bot.GetMonsters().Where(card => card.IsFacedown() && !targetList.Contains(card)).ToList())); + targetList.AddRange(Bot.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card)).OrderBy(card => card.Attack)); + AI.SelectCard(Card.Overlays); + Logger.DebugWriteLine("TYPHON first target: " + targetList[0]?.Name ?? "UNKNOWN"); + AI.SelectNextCard(targetList); + return true; + } + + return false; + } + + public bool UnchainedAbominationSpSummon() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + if (Enemy.GetMonsterCount() > 0 && Bot.HasInMonstersZone(CardId.UnchainedSoulOfAnguish) && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish)) return false; + List> usableMaterialMultiList = new List>(); + // anguish + 1 + ClientCard anguish = Bot.GetMonsters().FirstOrDefault(card => card.IsCode(CardId.UnchainedSoulOfAnguish)); + if (anguish != null) + { + List materials = GetCanBeUsedForLinkMaterial(true, card => card == anguish); + if (materials.Count() > 0) + { + usableMaterialMultiList.Add(new List { anguish, materials[0] }); + } + } + // link2 + 1 + 1 or link2 + link2 + List link2List = Bot.GetMonsters().Where(card => card.HasType(CardType.Link) && card.LinkCount == 2 + && !(card.IsCode(CardId.MuckrakerFromTheUnderworld) && summonThisTurn.Contains(card))).OrderBy(card => card.Attack).ToList(); + if (link2List.Count() > 0) + { + ClientCard link2Material = null; + ClientCard littleKnight = link2List.FirstOrDefault(card => card.Sequence >= 5 && card.IsCode(CardId.SPLittleKnight)); + if (littleKnight != null) link2Material = littleKnight; + else link2Material = link2List[0]; + if (link2List.Count() >= 2) + { + usableMaterialMultiList.Add(new List { link2Material, link2List.FirstOrDefault(card => card != link2Material) }); + } + List remainList = GetCanBeUsedForLinkMaterial(false, card => card != link2Material && !(card.HasType(CardType.Link) && card.LinkMarker > 2)); + if (remainList.Count() >= 2) + { + usableMaterialMultiList.Add(new List { link2Material, remainList[0], remainList[1] }); + } + } + + // check material list + foreach (List currMaterials in usableMaterialMultiList) + { + bool summonFlag = CheckCanDirectAttack() && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(currMaterials) + 3000 >= Enemy.LifePoints; + summonFlag |= GetProblematicEnemyMonster(0) != null && GetProblematicEnemyMonster(3000) == null; + + if (summonFlag) + { + AI.SelectMaterials(currMaterials); + return true; + } + } + + return false; + } + public bool UnchainedAbominationActivate() + { + if (CheckWhetherNegated()) return false; + List targetList = GetNormalEnemyTargetList(true, true, CardType.Monster); + if (targetList.Count() == 0) return false; + long logDesc = ActivateDescription; + if (logDesc >= Util.GetStringId(CardId.UnchainedAbomination, 0)) + { + logDesc = Util.GetStringId(CardId.UnchainedAbomination, 0) - 10; + } + Logger.DebugWriteLine("[UnchainedAbomination]desc: " + logDesc + ", timing = " + CurrentTiming); + if (ActivateDescription == Util.GetStringId(CardId.UnchainedAbomination, 0)) activatedCardIdList.Add(Card.Id); + if (ActivateDescription == Util.GetStringId(CardId.UnchainedAbomination, 1) || ActivateDescription == -1) activatedCardIdList.Add(Card.Id + 1); + if (ActivateDescription == Util.GetStringId(CardId.UnchainedAbomination, 2)) activatedCardIdList.Add(Card.Id + 2); + AI.SelectCard(targetList); + + return true; + } + + public bool UnchainedSoulOfAnguishSpSummon() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + + ClientCard unchainedNonLink = Bot.GetMonsters().FirstOrDefault(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained) && !card.HasType(CardType.Link)); + ClientCard unchainedLink2 = Bot.GetMonsters().FirstOrDefault(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained) && card.HasType(CardType.Link) && card.LinkCount == 2); + Logger.DebugWriteLine("[Anguish summon] unchainedNonLink = " + unchainedNonLink?.Name + ", unchainedLink2 = " + unchainedLink2?.Name); + if (unchainedNonLink == null && unchainedLink2 == null) return false; + int needMonsterCount = 2; + if (unchainedLink2 != null) needMonsterCount = 1; + if (needMonsterCount == 2 && Bot.HasInExtra(CardId.UnchainedSoulLordOfYama)) return false; + bool needAnguish = !Bot.HasInMonstersZone(CardId.UnchainedSoulOfAnguish) && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish) + && Enemy.GetMonsters().Any(card => card.IsFaceup()); + if (needAnguish) + { + needAnguish = Bot.HasInExtra(CardId.UnchainedSoulOfRage); + needAnguish |= Bot.HasInExtra(CardId.UnchainedAbomination); + needAnguish |= Bot.HasInExtra(CardId.SPLittleKnight) && banSpSummonExceptFiendCount == 0; + } + Logger.DebugWriteLine("[Anguish summon] needAnguish = " + needAnguish.ToString()); + + // check material + if (needMonsterCount == 1) + { + List materialList = GetCanBeUsedForLinkMaterial(needAnguish, card => card == unchainedLink2); + Logger.DebugWriteLine("[Anguish summon 1] material count = " + materialList.Count().ToString()); + if (materialList.Count() == 0) return false; + List selectMaterials = new List{ unchainedLink2, materialList[0]}; + bool summonFlag = needAnguish; + summonFlag |= CheckCanDirectAttack() && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(selectMaterials) + 2400 >= Enemy.LifePoints; + Logger.DebugWriteLine("[Anguish summon 1] summon flag " + summonFlag.ToString()); + if (summonFlag) + { + AI.SelectMaterials(selectMaterials); + return true; + } + } + if (needMonsterCount == 2) + { + List materialList = GetCanBeUsedForLinkMaterial(needAnguish, card => card == unchainedNonLink); + Logger.DebugWriteLine("[Anguish summon 2] material count = " + materialList.Count().ToString()); + if (materialList.Count() >= 2) + { + List selectMaterials = new List { unchainedNonLink, materialList[0], materialList[1] }; + if (needAnguish || GetMaterialAttack(selectMaterials) < 2400) + { + AI.SelectMaterials(selectMaterials); + return true; + } + } + } + + return false; + } + public bool UnchainedSoulOfAnguishActivate() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated()) return false; + List targetList = Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeMonsterTarget()).OrderByDescending(card => card.Attack).ToList(); + if (targetList.Count() > 0) + { + currentDestroyCardList.Add(targetList[0]); + int summonId = 0; + if (Bot.HasInExtra(CardId.UnchainedAbomination) && GetProblematicEnemyMonster(3000, ignoreCurrentDestroy:true) == null) + summonId = CardId.UnchainedAbomination; + else if (banSpSummonExceptFiendCount == 0 && Bot.HasInExtra(CardId.SPLittleKnight) && GetProblematicEnemyCardList(true, false, CardType.Monster).Count() > 0) + summonId = CardId.SPLittleKnight; + else if (Bot.HasInExtra(CardId.UnchainedSoulOfRage)) summonId = CardId.UnchainedSoulOfRage; + if (summonId > 0) + { + List materialList = new List(targetList){Card}; + Logger.DebugWriteLine("*** Anguish select: " + summonId.ToString()); + + AI.SelectCard(targetList); + AI.SelectNextCard(summonId); + AI.SelectMaterials(materialList); + activatedCardIdList.Add(Card.Id); + } + return true; + } + } + if (Card.Location == CardLocation.Grave) + { + return UnchainRecycleActivate(); + } + + return false; + } + + public bool UnchainedSoulLordOfYamaSpSummon() + { + if (CheckShouldNoMoreSpSummon(false)) return false; + if (Bot.HasInMonstersZone(CardId.UnchainedSoulLordOfYama) || activatedCardIdList.Contains(CardId.UnchainedSoulLordOfYama)) return false; + + bool need3Monster = Bot.HasInExtra(CardId.UnchainedSoulOfAnguish) && !Bot.HasInMonstersZone(CardId.UnchainedSoulOfAnguish) + && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish) && GetProblematicEnemyMonster(canBeTarget:true, selfType: CardType.Monster) != null; + need3Monster |= CheckAtAdvantage() && Duel.Phase == DuelPhase.Main2 + && Bot.HasInExtra(CardId.UnchainedSoulOfRage) && !Bot.HasInMonstersZone(CardId.UnchainedSoulOfRage); + bool haveUnchainSoul = Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained)); + if (need3Monster) + { + need3Monster = Bot.HasInExtra(CardId.UnchainedSoulOfRage); + need3Monster |= Bot.HasInExtra(CardId.UnchainedAbomination); + need3Monster |= Bot.HasInExtra(CardId.SPLittleKnight) && banSpSummonExceptFiendCount == 0; + } + // check material + List materialList = GetCanBeUsedForLinkMaterial(need3Monster, + card => !card.HasRace(CardRace.Fiend) || (card.HasType(CardType.Link) && card.HasSetcode(SetcodeUnchained))); + Logger.DebugWriteLine("[Yama Summon]need3Monster = " + need3Monster.ToString() + ", material count = " + materialList.Count()); + for (int index1 = 0; index1 < materialList.Count() - 1; ++ index1) + { + ClientCard material1 = materialList[index1]; + for (int index2 = index1 + 1; index2 < materialList.Count(); ++ index2) + { + ClientCard material2 = materialList[index2]; + List selectMaterials = new List{material1, material2}; + if (need3Monster && materialList.Count() == 2 && (activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara) || Bot.GetSpells().Count() == 0)) + { + // only for attack + if (GetProblematicEnemyMonster() != null || !CheckCanDirectAttack() || GetMaterialAttack(selectMaterials) >= 2000) return false; + } + bool summonFlag = need3Monster; + summonFlag |= Enemy.GetMonsterCount() == 0 && GetMaterialAttack(selectMaterials) < 2000; + summonFlag |= CheckAtAdvantage() && !haveUnchainSoul; + if (summonFlag) + { + AI.SelectMaterials(selectMaterials); + return true; + } + } + } + + return false; + } + public bool UnchainedSoulLordOfYamaActivate() + { + if (Card.Location == CardLocation.MonsterZone && (ActivateDescription == Util.GetStringId(CardId.UnchainedSoulLordOfYama, 0) || ActivateDescription == -1)) + { + // search + if (CheckWhetherNegated()) return false; + AI.SelectCard(CardId.UnchainedSoulOfSharvara, CardId.UnchainedAbomination, CardId.UnchainedSoulOfAnguish, CardId.UnchainedSoulOfRage); + activatedCardIdList.Add(Card.Id); + return true; + } + else if (Card.Location == CardLocation.Grave) + { + // spsummon & destroy + ClientCard chaosAngel = null; + ClientCard abomination = null; + ClientCard lady = null; + ClientCard lovely = null; + ClientCard arianna = null; + ClientCard bestAttack = null; + ClientCard rage = null; + foreach (ClientCard grave in Bot.Graveyard) + { + if (grave.IsCode(CardId.ChaosAngel) && grave.ProcCompleted != 0 && !dimensionalBarrierAnnouced.Contains(HintMsg.SYNCHRO)) chaosAngel = grave; + if (grave.IsCode(CardId.UnchainedSoulOfRage) && grave.ProcCompleted != 0) rage = grave; + if (grave.IsCode(CardId.UnchainedAbomination) && grave.ProcCompleted != 0) abomination = grave; + if (grave.IsCode(CardId.LadyLabrynthOfTheSilverCastle)) lady = grave; + if (grave.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) lovely = grave; + if (grave.IsCode(CardId.AriannaTheLabrynthServant)) arianna = grave; + if (Card != grave && grave.IsMonster() && grave.HasRace(CardRace.Fiend)) + { + if (!grave.IsCanRevive()) continue; + if (bestAttack == null || grave.Attack > bestAttack.Attack) bestAttack = grave; + } + } + + ClientCard select = null; + bool destroyWelcome = false; + if (chaosAngel != null && (GetProblematicEnemyCardList(selfType: CardType.Monster).Count() > 0 || + (Bot.GetMonsterCount() == 0 && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) + { + select = chaosAngel; + } + if (select == null && abomination != null && (GetProblematicEnemyCardList(selfType: CardType.Monster).Count() > 0 || + (Bot.GetMonsterCount() == 0 && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) + { + select = abomination; + Logger.DebugWriteLine("[Yama] timing: " + CurrentTiming.ToString()); + if (Bot.HasInSpellZone(CardId.WelcomeLabrynth) && !(Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + && !activatedCardIdList.Contains(CardId.UnchainedAbomination)) + { + destroyWelcome = true; + } + } + if (select == null && rage != null && (Duel.Player == 0 || (!activatedCardIdList.Contains(CardId.UnchainedSoulOfRage) && (Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2))) + && Bot.HasInExtra(new List { CardId.UnchainedSoulOfAnguish, CardId.SPLittleKnight })) select = rage; + if (select == null && arianna != null && Duel.Player == 0 && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant)) select = arianna; + if (select == null && lovely != null && Duel.Player == 1 && Util.GetBestAttack(Enemy) < 2900) select = lovely; + if (select == null && lady != null && Duel.Player == 1 && Util.GetBestAttack(Enemy) < 3000) select = lady; + if (select == null && arianna != null && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant)) select = arianna; + if (select == null && bestAttack != null) select = bestAttack; + + if (select != null) + { + activatedCardIdList.Add(Card.Id + 1); + AI.SelectCard(select); + if (destroyWelcome) + { + AI.SelectYesNo(true); + AI.SelectNextCard(CardId.WelcomeLabrynth); + } else { + AI.SelectYesNo(false); + } + return true; + } + } + + return false; + } + + public bool UnchainedSoulOfRageSpSummon() + { + if (CheckShouldNoMoreSpSummon(false) || CheckWhetherNegated(true, true, CardType.Monster | CardType.Link)) return false; + if (Bot.HasInMonstersZone(CardId.UnchainedSoulOfRage)) return false; + + ClientCard unchained = Bot.GetMonsters().FirstOrDefault(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained) + && !card.IsCode(CardId.UnchainedSoulOfAnguish, CardId.UnchainedAbomination)); + if (unchained == null) return false; + + bool summonFlag = CheckAtAdvantage() && Util.IsTurn1OrMain2(); + summonFlag |= !(Bot.HasInExtra(CardId.UnchainedSoulOfAnguish) && !activatedCardIdList.Contains(CardId.UnchainedSoulOfAnguish)) && Util.IsTurn1OrMain2(); + if (summonFlag) + { + summonFlag = Bot.HasInExtra(CardId.UnchainedSoulOfAnguish); + summonFlag |= Bot.HasInExtra(CardId.SPLittleKnight) && banSpSummonExceptFiendCount == 0; + } + + List materialList = GetCanBeUsedForLinkMaterial(Util.IsTurn1OrMain2(), + card => !card.HasRace(CardRace.Fiend) || card == unchained); + if (materialList.Count() > 0) + { + List selectMaterials = new List{unchained, materialList[0]}; + summonFlag |= Enemy.GetMonsterCount() == 0 && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(selectMaterials) + 1800 >= Enemy.LifePoints; + if (summonFlag) + { + AI.SelectMaterials(selectMaterials); + return true; + } + } + + return false; + } + public bool UnchainedSoulOfRageActivate() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (CheckWhetherNegated()) return false; + bool activateFlag = DefaultOnBecomeTarget() && !Util.ChainContainsCard(CardId.EscapeOfTheUnchained); + ClientCard problemMonster = GetProblematicEnemyMonster(-1, true, true, CardType.Monster); + List targetList = Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeTarget()).OrderBy(card => card.Attack).ToList(); + if (problemMonster != null) targetList.Insert(0, problemMonster); + + activateFlag |= (CurrentTiming & hintTimingMainEnd) > 0 && Util.IsOneEnemyBetterThanValue(Card.Attack, true); + activateFlag |= problemMonster != null; + + if (activateFlag && targetList.Count() > 0) + { + ClientCard target = targetList[0]; + int summonId = 0; + if (Bot.HasInExtra(CardId.UnchainedAbomination) && GetProblematicEnemyMonster(3000) == null + && target.HasType(CardType.Link) && target.LinkCount == 2) summonId = CardId.UnchainedSoulOfAnguish; + else if (banSpSummonExceptFiendCount == 0 && Bot.HasInExtra(CardId.SPLittleKnight)) summonId = CardId.SPLittleKnight; + else if (Bot.HasInExtra(CardId.UnchainedSoulOfAnguish) && GetProblematicEnemyMonster(2400) == null) summonId = CardId.UnchainedSoulOfAnguish; + List materialList = new List(targetList){Card}; + + AI.SelectCard(targetList); + AI.SelectNextCard(summonId); + AI.SelectMaterials(materialList); + activatedCardIdList.Add(Card.Id); + escapeTargetList.Add(Card); + currentDestroyCardList.Add(target); + return true; + } + } + if (Card.Location == CardLocation.Grave) + { + return UnchainRecycleActivate(); + } + + return false; + } + + public bool UnchainRecycleActivate() + { + AI.SelectCard(CardId.UnchainedSoulOfSharvara, CardId.LovelyLabrynthOfTheSilverCastle, CardId.AriannaTheLabrynthServant, + CardId.UnchainedAbomination, CardId.LabrynthStovieTorbie, CardId.LabrynthChandraglier, CardId.LabrynthCooclock, + CardId.AriasTheLabrynthButler, CardId.ArianeTheLabrynthServant); + activatedCardIdList.Add(Card.Id + 1); + + return true; + } + + public bool SPLittleKnightSpSummon() + { + if (CheckCanDirectAttack()) + { + // for attack + List materialList = SPLittleKnightSelectMaterial(); + if (materialList.Count() >= 2 && GetMaterialAttack(materialList) < 1600) + { + AI.SelectMaterials(materialList); + return true; + } + } else if (!CheckWhetherNegated(true, true, CardType.Monster | CardType.Link) && GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0) + { + // for remove + List materialList = SPLittleKnightSelectMaterial(true); + if (materialList.Count() >= 2 && materialList.Any(card => card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link))) + { + AI.SelectMaterials(materialList); + return true; + } + } + return false; + } + public List SPLittleKnightSelectMaterial(bool needToUseEffect = false) + { + List usedMaterialList = new List(); + if (Bot.GetMonstersExtraZoneCount() > 0) + { + ClientCard botMonsterExtraZome = Bot.GetMonstersInExtraZone()[0]; + if (botMonsterExtraZome.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Pendulum) || botMonsterExtraZome.IsCode(CardId.RelinquishedAnima)) + { + usedMaterialList.Add(botMonsterExtraZome); + if (botMonsterExtraZome.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)) needToUseEffect = false; + } + List materialList = GetCanBeUsedForLinkMaterial(true, card => card == botMonsterExtraZome); + if (materialList.Count() > 0) + { + foreach (ClientCard card in materialList) + { + if (!needToUseEffect || card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz) || (card.HasType(CardType.Link) && card.LinkCount <= 2)) + { + usedMaterialList.Add(card); + if (card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link)) needToUseEffect = false; + } + if (usedMaterialList.Count() >= 2) break; + } + } + if (usedMaterialList.Count() < 2) usedMaterialList.Clear(); + } else { + List materialList = GetCanBeUsedForLinkMaterial(true, card => !needToUseEffect + || card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz) || (card.HasType(CardType.Link) && card.LinkCount <= 2)); + if (materialList.Count() >= 2) + { + for (int idx1 = 0; idx1 < materialList.Count() - 1; ++ idx1) + { + ClientCard material1 = materialList[idx1]; + if (material1.HasType(CardType.Link) && material1.LinkCount >= 3) continue; + bool flag1 = !needToUseEffect || material1.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link); + for (int idx2 = 0; idx2 < materialList.Count(); ++ idx2) + { + ClientCard material2 = materialList[idx2]; + if (material2.HasType(CardType.Link) && material2.LinkCount >= 3) continue; + bool flag2 = !needToUseEffect || material2.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link); + if (flag1 || flag2) + { + return new List{material1, material2}; + } + } + } + } + } + + return usedMaterialList; + } + + public bool SPLittleKnightActivate() + { + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.SPLittleKnight, 0)) + { + // banish card + List problemCardList = GetProblematicEnemyCardList(true, selfType: CardType.Monster); + problemCardList.AddRange(GetDangerousCardinEnemyGrave(false)); + problemCardList.AddRange(GetNormalEnemyTargetList(true, true, CardType.Monster)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => card.HasType(CardType.Monster)).OrderByDescending(card => card.Attack)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => !card.HasType(CardType.Monster))); + if (problemCardList.Count() > 0) + { + AI.SelectCard(problemCardList); + activatedCardIdList.Add(Card.Id); + return true; + } + } else if (ActivateDescription == Util.GetStringId(CardId.SPLittleKnight, 1)) + { + ClientCard selfMonster = null; + foreach (ClientCard target in Bot.GetMonsters()) + { + if (Duel.ChainTargets.Contains(target) && !escapeTargetList.Contains(target)) + { + selfMonster = target; + break; + } + } + if (selfMonster == null) + { + if (Duel.Player == 1) + { + selfMonster = Bot.GetMonsters().Where(card => card.IsAttack()).OrderBy(card => card.Attack).FirstOrDefault(); + if (!Util.IsOneEnemyBetterThanValue(selfMonster.Attack, true)) selfMonster = null; + } + } + if (selfMonster != null) + { + ClientCard nextMonster = null; + List selfTargetList = Bot.GetMonsters().Where(card => card != selfMonster).ToList(); + if (Enemy.GetMonsterCount() == 0 && selfTargetList.Count() > 0) + { + selfTargetList.Sort(CompareUsableAttack); + nextMonster = selfTargetList[0]; + escapeTargetList.Add(nextMonster); + } + if (Enemy.GetMonsterCount() > 0) + { + nextMonster = GetBestEnemyMonster(false, true, true); + currentDestroyCardList.Add(nextMonster); + } + if (nextMonster != null) + { + AI.SelectCard(selfMonster); + AI.SelectNextCard(nextMonster); + escapeTargetList.Add(selfMonster); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + + return false; + } + + public bool MuckrakerFromTheUnderworldSpSummon() + { + List materialList = GetCanBeUsedForLinkMaterial(true, card => card.HasType(CardType.Link)); + if (materialList.Count() < 2) return false; + bool willBeNegated = CheckWhetherNegated(true, true, CardType.Monster | CardType.Link) && Bot.Hand.Count() > 0; + bool canRebornAngel = Bot.Graveyard.Any(card => card.IsCanRevive() && card.IsCode(CardId.ChaosAngel)) && !willBeNegated; + bool canRebornLovely = Bot.Graveyard.Any(card => card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) && !willBeNegated; + int bestAttackGrave = 0; + bool chaosAngelFlag = GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0 && !CheckWhetherNegated(true, true, CardType.Monster); + foreach (ClientCard grave in Bot.Graveyard) + { + if (grave.IsMonster() && grave.HasRace(CardRace.Fiend)) + { + if (!grave.IsCanRevive()) continue; + if (grave.Attack > bestAttackGrave) bestAttackGrave = grave.Attack; + } + } + for (int idx1 = 0; idx1 < materialList.Count() - 1; ++ idx1) + { + ClientCard material1 = materialList[idx1]; + for (int idx2 = idx1 + 1; idx2 < materialList.Count(); ++ idx2) + { + ClientCard material2 = materialList[idx1]; + List currentList = new List { material1, material2 }; + bool summonFlag = chaosAngelFlag && (canRebornAngel || (currentList.Any(card => card.IsCode(CardId.ChaosAngel)) && !willBeNegated)); + summonFlag |= Enemy.GetMonsterCount() == 0 && canRebornLovely; + summonFlag |= !activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle) && Bot.Graveyard.Any(card => card.Type == (int)CardType.Trap) + && currentList.Any(card => card.IsDisabled() && card.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)); + if (CheckCanDirectAttack()) + { + summonFlag |= GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack(currentList) + bestAttackGrave >= Enemy.LifePoints; + summonFlag |= GetMaterialAttack(currentList) < 1000; + } + if (summonFlag) + { + AI.SelectMaterials(currentList); + return true; + } + } + } + return false; + } + public bool MuckrakerFromTheUnderworldActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.MuckrakerFromTheUnderworld, 0)) + { + if (CheckWhetherNegated()) return false; + ClientCard chaosAngel = null; + ClientCard lovely = null; + ClientCard arianna = null; + ClientCard bestAttack = null; + foreach (ClientCard grave in Bot.Graveyard) + { + if (grave.IsCode(CardId.ChaosAngel) && grave.ProcCompleted != 0 && !dimensionalBarrierAnnouced.Contains(HintMsg.SYNCHRO)) chaosAngel = grave; + if (grave.IsCode(CardId.LovelyLabrynthOfTheSilverCastle)) lovely = grave; + if (grave.IsCode(CardId.AriannaTheLabrynthServant)) arianna = grave; + if (Card != grave && grave.IsMonster() && grave.HasRace(CardRace.Fiend)) + { + if (!grave.IsCanRevive()) continue; + if (bestAttack == null || grave.Attack > bestAttack.Attack) bestAttack = grave; + } + } + + ClientCard rebornTarget = null; + if (chaosAngel != null && (GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0)) rebornTarget = chaosAngel; + if (rebornTarget == null && lovely != null && Util.GetBestAttack(Enemy) < 2900 && + (!activatedCardIdList.Contains(CardId.LovelyLabrynthOfTheSilverCastle) || Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth))) rebornTarget = lovely; + if (rebornTarget == null && bestAttack != null && CheckCanDirectAttack() + && GetBotCurrentTotalAttack() < Enemy.LifePoints && GetBotCurrentTotalAttack() + bestAttack.Attack >= Enemy.LifePoints) rebornTarget = bestAttack; + if (rebornTarget == null && arianna != null && Duel.Player == 0 && !activatedCardIdList.Contains(CardId.AriannaTheLabrynthServant)) rebornTarget = arianna; + if (rebornTarget == null && bestAttack != null) rebornTarget = bestAttack; + if (rebornTarget != null) + { + AI.SelectCard(rebornTarget); + AI.SelectNextCard(FurnitureGetCost()); + activatedCardIdList.Contains(Card.Id); + banSpSummonExceptFiendCount = Math.Max(1, banSpSummonExceptFiendCount); + return true; + } + } + + return false; + } + + public bool RelinquishedAnimaSpSummon() + { + if (CheckWhetherNegated()) return false; + // summon to use effect + ClientCard enemyLeftEx = Enemy.MonsterZone[6]; + if (enemyLeftEx != null && enemyLeftEx.HasLinkMarker((int)CardLinkMarker.Top) && !enemyLeftEx.IsShouldNotBeTarget() && !enemyLeftEx.IsShouldNotBeMonsterTarget()) + { + ClientCard selfMonsterZone1 = Bot.MonsterZone[1]; + if (selfMonsterZone1 == null) + { + AI.SelectMaterials(CardId.LabrynthCooclock); + AI.SelectPlace(Zones.z1); + return true; + } + else if (!selfMonsterZone1.HasType(CardType.Xyz | CardType.Link | CardType.Token) && selfMonsterZone1.Level == 1) + { + AI.SelectMaterials(selfMonsterZone1); + AI.SelectPlace(Zones.z1); + return true; + } + } + ClientCard enemyRightEx = Enemy.MonsterZone[5]; + if (enemyRightEx != null && enemyRightEx.HasLinkMarker((int)CardLinkMarker.Top) && !enemyRightEx.IsShouldNotBeTarget() && !enemyRightEx.IsShouldNotBeMonsterTarget()) + { + ClientCard selfMonsterZone3 = Bot.MonsterZone[3]; + if (selfMonsterZone3 == null) + { + AI.SelectMaterials(CardId.LabrynthCooclock); + AI.SelectPlace(Zones.z3); + return true; + } + else if (!selfMonsterZone3.HasType(CardType.Xyz | CardType.Link | CardType.Token) && selfMonsterZone3.Level == 1) + { + AI.SelectMaterials(selfMonsterZone3); + AI.SelectPlace(Zones.z3); + return true; + } + } + + if (Bot.MonsterZone[5] != null || Bot.MonsterZone[6] != null) return false; + ClientCard enemyMonsterLeft = Enemy.MonsterZone[3]; + ClientCard enemyMonsterRight = Enemy.MonsterZone[1]; + if (Enemy.MonsterZone[6] != null) enemyMonsterLeft = null; + if (enemyMonsterLeft != null && enemyMonsterLeft.IsFacedown()) enemyMonsterLeft = null; + if (enemyMonsterLeft != null && (enemyMonsterLeft.IsShouldNotBeMonsterTarget() || enemyMonsterLeft.IsShouldNotBeTarget())) enemyMonsterLeft = null; + + if (Enemy.MonsterZone[5] != null) enemyMonsterRight = null; + if (enemyMonsterRight != null && (enemyMonsterRight.IsShouldNotBeMonsterTarget() || enemyMonsterRight.IsShouldNotBeTarget())) enemyMonsterRight = null; + if (enemyMonsterRight != null && enemyMonsterRight.IsFacedown()) enemyMonsterRight = null; + + int place = -1; + if (enemyMonsterLeft != null && enemyMonsterRight == null) place = Zones.z5; + if (enemyMonsterLeft == null && enemyMonsterRight != null) place = Zones.z6; + if (enemyMonsterLeft != null && enemyMonsterRight != null) + { + if (enemyMonsterLeft.IsFloodgate() && !enemyMonsterRight.IsFloodgate()) place = Zones.z5; + else if (!enemyMonsterLeft.IsFloodgate() && enemyMonsterRight.IsFloodgate()) place = Zones.z6; + else + { + if (enemyMonsterLeft.GetDefensePower() >= enemyMonsterRight.GetDefensePower()) place = Zones.z5; + else place = Zones.z6; + } + } + if (place >= 0) + { + AI.SelectMaterials(Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link | CardType.Token) && card.Level == 1) + .OrderBy(card => card.Attack).ToList()); + AI.SelectPlace(place); + return true; + } + + // summon for little knight + if (Bot.HasInExtra(CardId.SPLittleKnight) && Bot.GetMonsters().Count(card => card.IsFaceup()) >= 2 + && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link))) + { + if (GetProblematicEnemyCardList(true, selfType: CardType.Monster).Count() > 0) + { + AI.SelectMaterials(Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link | CardType.Token) && card.Level == 1) + .OrderBy(card => card.Attack).ToList()); + return true; + } + } + return false; + } + public bool RelinquishedAnimaActivate() + { + if (CheckWhetherNegated()) return false; + activatedCardIdList.Add(Card.Id); + Dictionary placeList = new Dictionary{ {1, 6}, {3, 5}, {5, 3}, {6, 1} }; + foreach (KeyValuePair placePair in placeList) + { + if (Bot.MonsterZone[placePair.Key] == Card && Enemy.MonsterZone[placePair.Value] != null) + { + currentDestroyCardList.Add(Enemy.MonsterZone[placePair.Value]); + break; + } + } + return true; + } + + public bool MonsterRepos() + { + int selfAttack = Card.Attack + 1; + + if (selfAttack <= 1) + return !Card.IsDefense(); + + int bestAttack = 0; + foreach (ClientCard card in Bot.GetMonsters()) + { + int attack = card.Attack; + if (attack >= bestAttack) + { + bestAttack = attack; + } + } + + bool enemyBetter = Util.IsAllEnemyBetterThanValue(bestAttack, true); + + if (Card.IsAttack() && enemyBetter) + return true; + if (Card.IsDefense() && !enemyBetter) + return true; + return false; + } + + public bool ReposForLabrynth() + { + if (!activatedCardIdList.Contains(CardId.BigWelcomeLabrynth) && Bot.HasInSpellZoneOrInGraveyard(CardId.BigWelcomeLabrynth)) + return Card.IsFacedown(); + return false; + } + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + if (Card.IsCode(CardId.BigWelcomeLabrynth) && Bot.HasInSpellZone(Card.Id)) return false; + if (Card.IsCode(CardId.TransactionRollback) && !Bot.HasInSpellZone(CardId.TransactionRollback)) + { + // check enemy grave trap + bool haveCopyTrap = false; + if (Enemy.Graveyard.Any(card => card.IsCode( + CardId.WelcomeLabrynth, CardId.BigWelcomeLabrynth, _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier, CardId.DestructiveDarumaKarmaCannon, + _CardId.CompulsoryEvacuationDevice, _CardId.BreakthroughSkill + ))) + { + haveCopyTrap = true; + } + + if (!haveCopyTrap && !Bot.HasInHand(CardId.UnchainedSoulOfSharvara)) return false; + } + if (Card.IsCode(CardId.EscapeOfTheUnchained) && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeUnchained))) return false; + + // select place + if (Card.IsTrap() || Card.HasType(CardType.QuickPlay)) + { + List avoidList = new List(); + int setFornfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoidList.Add(4 - i); + setFornfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setFornfiniteImpermanence); + return true; + } else + { + SelectSTPlace(Card, false, avoidList); + return true; + } + } else + { + SelectSTPlace(); + } + return true; + } + + else if (Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true) || Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true)) + { + if (Card.IsSpell() && !Bot.HasInSpellZone(Card.Id)) + { + SelectSTPlace(); + return true; + } + } + + return false; + } + + public bool SpellSetForCooClockCheck() + { + // set to destroy for Sharvara + if (Card.IsCode(CardId.PotOfExtravagance, CardId.TransactionRollback, CardId.WelcomeLabrynth) && Bot.HasInHand(CardId.UnchainedSoulOfSharvara) + && !activatedCardIdList.Contains(CardId.UnchainedSoulOfSharvara)) + { + SelectSTPlace(Card, false); + return true; + } + // set to activate by cooclock + bool haveLabrynth = Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasSetcode(SetcodeLabrynth)); + if (!cooclockAffected || (haveLabrynth && Bot.HasInHand(CardId.LabrynthCooclock) && !activatedCardIdList.Contains(CardId.LabrynthCooclock))) return false; + if (!Card.IsCode(CardId.BigWelcomeLabrynth, CardId.WelcomeLabrynth, _CardId.InfiniteImpermanence, _CardId.DimensionalBarrier)) return false; + if (haveLabrynth) + { + SelectSTPlace(Card, true); + return true; + } + if (!Card.IsCode(CardId.BigWelcomeLabrynth, CardId.WelcomeLabrynth)) return false; + if (!summoned && Bot.Hand.Any(card => card.IsMonster() && card.Level <= 4 && card.HasSetcode(SetcodeLabrynth))) + { + SelectSTPlace(Card, true); + return true; + } + + return false; + } + } +} diff --git a/Game/AI/Decks/Level8Executor.cs b/Game/AI/Decks/Level8Executor.cs index 130a70b9..e35a5aeb 100644 --- a/Game/AI/Decks/Level8Executor.cs +++ b/Game/AI/Decks/Level8Executor.cs @@ -217,11 +217,12 @@ public override void OnNewTurn() JetSynchronUsed = false; ScrapWyvernUsed = false; MaskedChameleonUsed = false; + base.OnNewTurn(); } public override void OnChainEnd() { - + base.OnChainEnd(); } public override CardPosition OnSelectPosition(int cardId, IList positions) diff --git a/Game/AI/Decks/LightswornShaddoldinosourExecutor.cs b/Game/AI/Decks/LightswornShaddoldinosourExecutor.cs index d486d9f0..9b91d735 100644 --- a/Game/AI/Decks/LightswornShaddoldinosourExecutor.cs +++ b/Game/AI/Decks/LightswornShaddoldinosourExecutor.cs @@ -274,6 +274,7 @@ public override void OnNewTurn() ShaddollSquamata_used = false; ShaddollDragon_used = false; ShaddollHedgehog_used = false; + base.OnNewTurn(); } private bool Luminasummon() @@ -290,6 +291,7 @@ private bool Luminasummon() } private bool Luminaeff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Bot.HasInGraveyard(CardId.Raiden)) { AI.SelectCard(Useless_List()); @@ -555,6 +557,7 @@ private bool SouleatingOviraptoreff() private bool GlowUpBulbeff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; IList check = Bot.GetMonstersInExtraZone(); foreach (ClientCard monster in check) if (monster.HasType(CardType.Fusion)) return false; @@ -627,6 +630,7 @@ private bool PotofAvariceeff() private bool MaxxC() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Duel.Player == 1; } @@ -977,6 +981,7 @@ private bool FoolishBurialEffect() public bool Hand_act_eff() { //if (Card.IsCode(CardId.Urara) && Bot.HasInHand(CardId.LockBird) && Bot.HasInSpellZone(CardId.Re)) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Card.IsCode(CardId.GhostOgre) && Card.Location == CardLocation.Hand && Bot.HasInMonstersZone(CardId.GhostOgre)) return false; return (Duel.LastChainPlayer == 1); } @@ -998,6 +1003,7 @@ private bool Michaeleff() if (Card.Location == CardLocation.Grave) return true; if (Bot.LifePoints <= 1000) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; ClientCard select = Util.GetBestEnemyCard(); if (select == null) return false; if(select!=null) diff --git a/Game/AI/Decks/LuckyExecutor.cs b/Game/AI/Decks/LuckyExecutor.cs index 516ca26c..f4bffea7 100644 --- a/Game/AI/Decks/LuckyExecutor.cs +++ b/Game/AI/Decks/LuckyExecutor.cs @@ -1,4 +1,4 @@ -using YGOSharp.OCGWrapper.Enums; +using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using System.Linq; using WindBot; @@ -86,7 +86,7 @@ public LuckyExecutor(GameAI ai, Duel duel) private List HintMsgForEnemy = new List { HintMsg.Release, HintMsg.Destroy, HintMsg.Remove, HintMsg.ToGrave, HintMsg.ReturnToHand, HintMsg.ToDeck, - HintMsg.FusionMaterial, HintMsg.SynchroMaterial, HintMsg.XyzMaterial, HintMsg.LinkMaterial + HintMsg.FusionMaterial, HintMsg.SynchroMaterial, HintMsg.XyzMaterial, HintMsg.LinkMaterial, HintMsg.Disable }; private List HintMsgForDeck = new List @@ -219,11 +219,13 @@ public override CardPosition OnSelectPosition(int cardId, IList po private bool ImFeelingLucky() { + if (Type == ExecutorType.Activate && DefaultCheckWhetherCardIsNegated(Card)) return false; return Program.Rand.Next(10) >= 5 && DefaultDontChainMyself(); } private bool ImFeelingUnlucky() { + if (Type == ExecutorType.Activate && DefaultCheckWhetherCardIsNegated(Card)) return false; return DefaultDontChainMyself(); } @@ -239,4 +241,4 @@ private bool JustDontIt() return false; } } -} +} \ No newline at end of file diff --git a/Game/AI/Decks/MalissExecutor.cs b/Game/AI/Decks/MalissExecutor.cs new file mode 100644 index 00000000..99b2e225 --- /dev/null +++ b/Game/AI/Decks/MalissExecutor.cs @@ -0,0 +1,4206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +using YGOSharp.Network.Enums; +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Maliss", "AI_Maliss")] + public class MalissExecutor : DefaultExecutor + { + public class CardId + { + public const int DominusImpulse = 40366667; + public const int TERRAFORMING = 73628505; + //public const int AllureOfDarkness = 1475311; + public const int GoldSarcophagus = 75500286; + + // Cyberse / utility + public const int BackupIgnister = 30118811; + public const int WizardIgnister = 3723262; + + // Main Maliss pieces + public const int MalissP_Dormouse = 32061192; // Maliss

Dormouse + public const int MalissP_WhiteRabbit = 69272449; // Maliss

White Rabbit + public const int MalissP_ChessyCat = 96676583; // Maliss

Chessy Cat + public const int MalissP_MarchHare = 20938824; // Maliss

March Hare + public const int MalissC_GWC06 = 20726052; // Maliss GWC-06 + //public const int MalissC_TB11 = 57111661; // Maliss TB-11 + public const int MalissC_MTP07 = 94722358; // Maliss MTP-07 + public const int MalissQ_RedRansom = 68059897; // Maliss Red Ransom + public const int MalissQ_WhiteBinder = 95454996; // Maliss White Binder + public const int MalissQ_HeartsCrypter = 21848500; // Maliss Hearts Crypter + public const int MalissInTheMirror = 93453053; // Maliss in the Mirror (Spell) + public const int MalissInUnderground = 68337209; // Maliss in Underground + + + // === Extra Deck === + public const int Linguriboh = 24842059; + public const int LinkDecoder = 30342076; + public const int SP_LITTLE_KNIGHT = 29301450; + public const int SALAMANGREAT_ALMIRAJ = 60303245; + public const int SplashMage = 59859086; // Splash Mage + public const int CyberseWicckid = 52698008; // Cyberse Wicckid + public const int TranscodeTalker = 46947713; // Transcode Talker + public const int AlliedCodeTalkerIgnister = 39138610; // Allied Code Talker @Ignister + public const int FirewallDragon = 5043010; // Firewall Dragon + public const int LinkSpider = 98978921; // Link Spider + public const int HaggardLizardose = 9763474; // Haggard Lizardose + public const int AccesscodeTalker = 86066372; // Accesscode Talker + public const int Apollousa = 4280258; + + // === Handtraps / Others (blacklist/targets etc.) === + public const int Lancea = 34267821; + public const int Fuwalos = 42141493; + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + public const int SkillDrain = 82732705; + public const int DivineArsenalAAZEUS_SkyThunder = 90448279; + public const int DimensionShifter = 91800273; + public const int MacroCosmos = 30241314; + public const int DimensionalFissure = 81674782; + public const int BanisheroftheRadiance = 94853057; + public const int BanisheroftheLight = 61528025; + public const int KashtiraAriseHeart = 48626373; + public const int GhostMournerMoonlitChill = 52038441; + public const int NibiruThePrimalBeing = 27204311; + } + const int SetcodeMaliss = 0x1b9; + const int SetcodeTimeLord = 0x4a; + const int SetcodePhantom = 0xdb; + const int SetcodeOrcust = 0x11b; + const int SetcodeHorus = 0x3; + const int SetcodeDarkWorld = 0x6; + const int SetcodeSkyStriker = 0x115; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.MalissP_ChessyCat, CardId.MalissP_MarchHare,CardId.MalissP_WhiteRabbit,CardId.MalissInUnderground, + CardId.BackupIgnister, CardId.MalissP_Dormouse, + _CardId.AshBlossom,_CardId.InfiniteImpermanence,CardId.DominusImpulse } }, + {2, new List { _CardId.MaxxC, _CardId.CalledByTheGrave}}, + {1, new List { CardId.GoldSarcophagus, CardId.TERRAFORMING, + CardId.MalissC_GWC06, CardId.Lancea, CardId.MalissC_MTP07, + _CardId.CrossoutDesignator, CardId.MalissInTheMirror, CardId.WizardIgnister, + CardId.NibiruThePrimalBeing }} + }; + + List notToNegateIdList = new List { 58699500, 20343502, 19403423 }; + List notToDestroySpellTrap = new List { 50005218, 6767771 }; + List targetNegateIdList = new List { + _CardId.EffectVeiler, _CardId.InfiniteImpermanence, CardId.GhostMournerMoonlitChill, _CardId.BreakthroughSkill, 74003290, 67037924, + 9753964, 66192538, 23204029, 73445448, 35103106, 30286474, 45002991, 5795980, 38511382, 53742162, 30430448 + }; + + public MalissExecutor(GameAI ai, Duel duel) : base(ai, duel) + { + // Must Set First + AddExecutor(ExecutorType.SpellSet, CardId.MalissC_GWC06, SpellSetCheck); + AddExecutor(ExecutorType.SpellSet, CardId.MalissC_MTP07, SpellSetCheck); + + + AddExecutor(ExecutorType.Activate, CardId.AccesscodeTalker, Accesscode_OnSummon_AtkUp); + AddExecutor(ExecutorType.Activate, CardId.AccesscodeTalker, Accesscode_Destroy_Ignition); + + // ===== Generic counters ===== + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, _CardId.CrossoutDesignator, CrossoutDesignatorActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, CardId.Apollousa, DontSelfNG); + AddExecutor(ExecutorType.Activate, CardId.DominusImpulse, DontSelfNG); + AddExecutor(ExecutorType.Activate, CardId.AlliedCodeTalkerIgnister, Allied_NegateBanish); + AddExecutor(ExecutorType.Activate, CardId.FirewallDragon, FirewallBounce_OnOppSummon); + AddExecutor(ExecutorType.Activate, CardId.MalissC_MTP07, MTP07_OppTurn_RemoveEnemyOnly); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_HeartsCrypter, HC_Quick_ReturnBanished_AndBanishField); + AddExecutor(ExecutorType.Activate, CardId.MalissC_GWC06, GWC06_OppTurn_ReviveWB_HC); + AddExecutor(ExecutorType.Activate, CardId.SP_LITTLE_KNIGHT, ActLittleKnight); + + + AddExecutor(ExecutorType.Activate, CardId.MalissQ_RedRansom, RR_SS_FromBanished); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_HeartsCrypter, HC_OnBanished_SpecialSummon); + AddExecutor(ExecutorType.Activate, CardId.MalissInTheMirror, Mirror_Banish); + + // Plan#1 + // --- Start with Dormouse --- + AddExecutor(ExecutorType.Summon, CardId.MalissP_Dormouse, Step1_Dormouse_NormalSummon); + AddExecutor(ExecutorType.Summon, CardId.MalissP_WhiteRabbit, Step1_WhiteRabbit_NormalSummon); + AddExecutor(ExecutorType.Summon, CardId.MalissP_ChessyCat, TwoCC_NormalSummon); + AddExecutor(ExecutorType.Summon, CardId.MalissP_ChessyCat, Emergency_NormalCat); + AddExecutor(ExecutorType.Summon, CardId.MalissP_MarchHare, NSMH); + AddExecutor(ExecutorType.Summon, CardId.BackupIgnister, NSBackup_L); + AddExecutor(ExecutorType.Summon, CardId.BackupIgnister, NSBackup); + AddExecutor(ExecutorType.Activate, CardId.MalissP_Dormouse, Dormouse_SS_FromBanished); + AddExecutor(ExecutorType.Activate, CardId.MalissP_Dormouse, Dormouse_ForMH); + AddExecutor(ExecutorType.Activate, CardId.MalissP_Dormouse, Dormouse_Banish_Anytime); + AddExecutor(ExecutorType.Activate, CardId.MalissP_WhiteRabbit, Step1_WhiteRabbit_SS_FromBanished); + AddExecutor(ExecutorType.Activate, CardId.MalissP_WhiteRabbit, Step1_WhiteRabbit_SetTrapOnSummon); + AddExecutor(ExecutorType.Activate, CardId.MalissP_MarchHare, Step1_MH_FromHand); + AddExecutor(ExecutorType.Activate, CardId.MalissP_MarchHare, returnFromBanish); + AddExecutor(ExecutorType.SpSummon, CardId.Linguriboh, LinguribohMHLine); + AddExecutor(ExecutorType.SpSummon, CardId.LinkDecoder, Step1_SSLinkDecoder); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_RedRansom, Step2N_LinkSummon_RedRansom); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_RedRansom, Step2_LinkSummon_RedRansom); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_RedRansom, Step2_RedRansom_Search); + AddExecutor(ExecutorType.Activate, CardId.MalissP_ChessyCat, AnyDraw); + AddExecutor(ExecutorType.SpSummon, CardId.Apollousa, Link_Apo); + AddExecutor(ExecutorType.SpSummon, CardId.CyberseWicckid, Step2N_RRtoWicckid); + AddExecutor(ExecutorType.SpSummon, CardId.LinkDecoder, Step_SummonLinkDecoderToWicckid); + AddExecutor(ExecutorType.Activate, CardId.CyberseWicckid, Wicckid_SearchTuner); + AddExecutor(ExecutorType.SpSummon, CardId.SplashMage, Step_SplashToWB); + AddExecutor(ExecutorType.Activate, CardId.SplashMage, Step2N_SplashMage_ReviveP); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, Flow3_BackupIgnister_AfterMakeIt3); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, OneBody_Backup_SearchWizard); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_WhiteBinder, Step2N_LinkSummon_WB); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_WhiteBinder, WB_OnSummon_BanishGY); + AddExecutor(ExecutorType.Activate, CardId.MalissC_GWC06, GWC06_MyTurn_Extend); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_WhiteBinder, WB_SetMalissTrap); + + // === STEP2: 2 bodies -> Splash Mage -> revive P -> Red Ransom -> search === + AddExecutor(ExecutorType.Activate, CardId.WizardIgnister, Step2_Fallback_Wizard_AfterSplashNegated); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, Step2_Fallback_Backup_AfterSplashNegated); + + AddExecutor(ExecutorType.Activate, CardId.WizardIgnister, Flow3_WizardIgnister_AfterMakeIt3); + AddExecutor(ExecutorType.Activate, CardId.MalissQ_WhiteBinder, WB_OnBanished_SelfSS); + AddExecutor(ExecutorType.Activate, CardId.MalissP_MarchHare, ssFromHandMH); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_HeartsCrypter, Step_LinkSummon_HeartsCrypter); + AddExecutor(ExecutorType.SpSummon, CardId.AlliedCodeTalkerIgnister, Flow3_Link_Allied); + AddExecutor(ExecutorType.Activate, CardId.AlliedCodeTalkerIgnister, Allied_OnSummonTrigger); + + // Plan#2 White Rabbit --- + AddExecutor(ExecutorType.Activate, CardId.MalissC_MTP07, MTP07_ForMH); + AddExecutor(ExecutorType.SpSummon, CardId.FirewallDragon, Flow3_Link_Firewall); + AddExecutor(ExecutorType.SpSummon, CardId.MalissQ_WhiteBinder, Step_WicckidPlusOneToWB); + AddExecutor(ExecutorType.Activate, CardId.MalissP_ChessyCat, ChessyCat_SS_FromBanished); + + + AddExecutor(ExecutorType.SpSummon, CardId.CyberseWicckid, Step_RRtoWicckid); + AddExecutor(ExecutorType.Activate, CardId.MalissInUnderground, Flow3_UnderGround_Available_SSAnyPawn); + AddExecutor(ExecutorType.Activate, CardId.LinkDecoder, LinkDecoder_ReviveFromGY); + + // Fallback Lancea + AddExecutor(ExecutorType.SpSummon, CardId.TranscodeTalker, SummonTranscode); + AddExecutor(ExecutorType.Activate, CardId.TranscodeTalker, Transcode_ReviveLink3OrLower); + + // Emergency Start + AddExecutor(ExecutorType.Activate, CardId.TERRAFORMING, Terra_GrabUnderground); + AddExecutor(ExecutorType.Activate, CardId.GoldSarcophagus, GoldSarc_StartPiece); + AddExecutor(ExecutorType.Activate, CardId.MalissInUnderground, Underground_ActivateStarter); + + AddExecutor(ExecutorType.SpSummon, CardId.SplashMage, Step_SplashToRR); + AddExecutor(ExecutorType.Activate, CardId.SplashMage, Step2_SplashMage_ReviveP); + + // Fallback: Try to Start + AddExecutor(ExecutorType.SpSummon, CardId.LinkSpider); + AddExecutor(ExecutorType.Summon, _CardId.AshBlossom, Emergency_NS); + AddExecutor(ExecutorType.Summon, _CardId.MaxxC, Emergency_NS); + AddExecutor(ExecutorType.SpSummon, CardId.Linguriboh, OneBody_Link1_Linguriboh); + AddExecutor(ExecutorType.SpSummon, CardId.SALAMANGREAT_ALMIRAJ, OneBody_Link1_Almiraj); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, OneBody_Backup_SS); + AddExecutor(ExecutorType.Activate, CardId.BackupIgnister, OneBody_Backup_SearchWizard); + AddExecutor(ExecutorType.Activate, CardId.WizardIgnister, OneBody_Wizard_SS); + + + //Finisher + AddExecutor(ExecutorType.SpSummon, CardId.AccesscodeTalker, Flow3_Link_Accesscode); + + // Turn3 or More + AddExecutor(ExecutorType.SpSummon, CardId.Linguriboh, T3Allow); + AddExecutor(ExecutorType.SpSummon, CardId.SALAMANGREAT_ALMIRAJ, T3Allow); + AddExecutor(ExecutorType.SpSummon, CardId.TranscodeTalker, EmerTranscode); + AddExecutor(ExecutorType.SpSummon, CardId.AlliedCodeTalkerIgnister, Emer_Allied); + AddExecutor(ExecutorType.SpSummon, CardId.AlliedCodeTalkerIgnister, Emer_Allied2); + + // >>> Fallback: S:P Little Knight + AddExecutor(ExecutorType.SpSummon, CardId.SP_LITTLE_KNIGHT, SummonLittleKnightFast); + AddExecutor(ExecutorType.SpSummon, CardId.SP_LITTLE_KNIGHT, SPEmer); + + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + AddExecutor(ExecutorType.Repos, MonsterRepos); + + } + // Maliss Flags + bool usedNormalSummon = false; + bool ssDormouse = false; + bool ssWhiteRabbit = false; + bool ssChessyCat = false; + bool ssMarchHare = false; + bool ActiveMarchHare = false; + bool ssRRThisTurn = false; + bool ssWBThisTurn = false; + bool ssHCThisTurn = false; + bool enemyActivateLancea = false; + bool enemyActivateFuwalos = false; + bool ActiveUnderground = false; + bool blockWicckid = false; + bool mtp07SetThisTurn = false; + bool gwc06SetThisTurn = false; + bool splashNegatedThisTurn = false; + bool Allied_End = false; + bool fullBoard1 = false; + bool goldstart = false; + bool undergroundstart = false; + bool nsplan = false; + bool nsBackupplan = false; + bool NSDorMouse = false; + + int myTurnCount = 0; + bool avoidLinkedZones = false; + bool wantLinkedToWicckid = false; + private int? _wicckidEmzIndex = null; + private int _transcodeZoneMask = 0; + + const int MZ0 = 1 << 0; + const int MZ1 = 1 << 1; + const int MZ2 = 1 << 2; + const int MZ3 = 1 << 3; + const int MZ4 = 1 << 4; + const int EMZ_L = (1 << 5); + const int EMZ_R = (1 << 6); + const int EMZ_ALL = EMZ_L | EMZ_R; + int _wicckidEmzBit = 0; + int _forceTranscodeBit = 0; + + // Step Flag + bool step1Done = false; + bool step2Done = false; + int lastRevivedIdBySplash = 0; + bool coreSetupComplete = false; + bool madeIt3 = false; + bool resultSuccessFlag = false; + private bool _didSplashToRR; + private bool _didRRtoWicckid; + private bool _didSummonToWicckidArrow; + private bool _didWBFromWicckid; + private bool _finishPlanDecided; + private bool _preferWicckidArrows; + private bool _rrSelfSSPlacing = false; + private enum FinishPlan { FW_HC_Allied, HC_Allied, AlliedOnly } + private FinishPlan _finishPlan; + static bool IsEmzSeq(int seq) => seq >= 5; + static int BitOfSeq(int seq) => (1 << seq); + static int LowestBit(int m) => m & -m; + + private bool _oppJustActivatedPersistentSpell; + private bool _oppJustSummoned; + private bool _oppJustSet; + private int _prefWindowTTL; + + private int _enemyMonsterCountSnap; + private int _enemyFacedownSTSnap; + + //==================== Default code ==================== + #region Default Code Start Here + private int _totalAttack; + private int _totalBotAttack; + bool enemyActivateMaxxC = false; + bool enemyActivateLockBird = false; + int dimensionShifterCount = 0; + bool enemyActivateInfiniteImpermanenceFromHand = false; + List infiniteImpermanenceList = new List(); + List currentNegateCardList = new List(); + List currentDestroyCardList = new List(); + List sendToGYThisTurn = new List(); + List activatedCardIdList = new List(); + List enemyPlaceThisTurn = new List(); + List escapeTargetList = new List(); + List summonThisTurn = new List(); + + + public List ShuffleList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + T tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } + + public override bool OnSelectHand() { return true; } + + public List ShuffleCardList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(n + 1); + ClientCard temp = result[index]; + result[index] = result[n]; + result[n] = temp; + } + return result; + } + + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) + { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + public bool MonsterRepos() + { + int selfAttack = Card.Attack + 1; + + if (selfAttack <= 1) + return !Card.IsDefense(); + + int bestAttack = 0; + foreach (ClientCard card in Bot.GetMonsters()) + { + int attack = card.Attack; + if (attack >= bestAttack) + { + bestAttack = attack; + } + } + + bool enemyBetter = Util.IsAllEnemyBetterThanValue(bestAttack, true); + + if (Card.IsAttack() && enemyBetter) + return true; + if (Card.IsDefense() && !enemyBetter) + return true; + return false; + } + + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && Bot.GetMonsters().Any(card => card.IsFaceup())) + { + return true; + } + return false; + } + + public bool CheckInDanger() + { + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + int totalAtk = 0; + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsAttack() && !m.Attacked) totalAtk += m.Attack; + } + if (totalAtk >= Bot.LifePoints) return true; + } + return false; + } + private int GetMyLinkedMMZMask() + { + int mask = 0; + foreach (ClientCard m in Bot.GetMonsters()) + { + if (m == null || !m.IsFaceup() || !m.HasType(CardType.Link)) continue; + mask |= m.GetLinkedZones(); + } + mask &= 0x1F; + return mask; + } + private bool IsPawnId(long id) + { + return id == CardId.MalissP_Dormouse + || id == CardId.MalissP_WhiteRabbit + || id == CardId.MalissP_ChessyCat + || id == CardId.MalissP_MarchHare; + } + private int GetQueenLinkedMMZMask() + { + int mask = 0; + foreach (ClientCard m in Bot.GetMonsters()) + { + if (m == null || !m.IsFaceup()) continue; + + if (m.IsCode(CardId.MalissQ_RedRansom) + || m.IsCode(CardId.MalissQ_WhiteBinder) + || m.IsCode(CardId.MalissQ_HeartsCrypter)) + { + mask |= m.GetLinkedZones(); + } + } + mask &= 0x1F; + return mask; + } + + private int LinkValOf(ClientCard c) + { + return c.HasType(CardType.Link) ? Math.Max(1, c.LinkCount) : 1; + } + + private bool IsOneVal(ClientCard c) + { + return !c.HasType(CardType.Link) || Math.Max(1, c.LinkCount) == 1; + } + + private bool IsMaliss(ClientCard c) + { + return c.HasSetcode(SetcodeMaliss); + } + + private int ScoreForBanishedMaliss(ClientCard c) + { + if (c.IsCode(CardId.MalissInTheMirror)) return 100; + if (c.IsCode(CardId.MalissC_MTP07)) return 95; + if (c.IsCode(CardId.MalissC_GWC06)) return 90; + if (c.IsCode(CardId.MalissInUnderground)) return 85; + if (c.IsCode(CardId.MalissP_MarchHare)) return 80; + if (c.IsCode(CardId.MalissP_ChessyCat)) return 75; + if (c.IsCode(CardId.MalissP_WhiteRabbit)) return 70; + if (c.IsCode(CardId.MalissP_Dormouse)) return 65; + return 50; + } + + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone) + { + int MAIN_MASK = + (int)Zones.z0 | + (int)Zones.z1 | + (int)Zones.z2 | + (int)Zones.z3 | + (int)Zones.z4; + + int emzMask = available & ~MAIN_MASK; + int mainMask = available & MAIN_MASK; + if (IsPawnId(cardId)) + { + int queenMask = GetQueenLinkedMMZMask(); + int queenChoices = queenMask & available & MAIN_MASK; + + if (queenChoices != 0) + { + int pick = FirstBitFromOrder( + queenChoices, + new[] { (int)Zones.z2, (int)Zones.z1, (int)Zones.z3, (int)Zones.z0, (int)Zones.z4 } + ); + AI.SelectPlace(pick); + return pick; + } + } + if (cardId == CardId.AlliedCodeTalkerIgnister) + { + ClientCard fw = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.FirewallDragon)); + int emzAvail = available & EMZ_ALL; + + if (fw != null && fw.IsFaceup()) + { + bool firewallCenter = fw.Controller == 0 && fw.Location == CardLocation.MonsterZone && fw.Sequence == 2; + + int linkedAll = fw.GetLinkedZones(); + int linkedChoices = linkedAll & available; + + int linkedEmzChoices = linkedChoices & EMZ_ALL; + int pick; + + if (linkedEmzChoices != 0) + { + if (firewallCenter && (linkedEmzChoices & EMZ_L) != 0 && (linkedEmzChoices & EMZ_R) != 0) + { + int leftFree = 0; + if ((available & (int)Zones.z0) != 0) leftFree++; + if ((available & (int)Zones.z1) != 0) leftFree++; + + int rightFree = 0; + if ((available & (int)Zones.z3) != 0) rightFree++; + if ((available & (int)Zones.z4) != 0) rightFree++; + + if (leftFree > rightFree) + pick = EMZ_L; + else if (rightFree > leftFree) + pick = EMZ_R; + else + pick = FirstBitFromOrder(linkedEmzChoices, new[] { EMZ_L, EMZ_R }); + } + else + { + pick = FirstBitFromOrder(linkedEmzChoices, new[] { EMZ_L, EMZ_R }); + } + AI.SelectPlace(pick); + return pick; + } + if (emzAvail != 0) + { + if (firewallCenter && (emzAvail & EMZ_L) != 0 && (emzAvail & EMZ_R) != 0) + { + int leftFree = 0; + if ((available & (int)Zones.z0) != 0) leftFree++; + if ((available & (int)Zones.z1) != 0) leftFree++; + + int rightFree = 0; + if ((available & (int)Zones.z3) != 0) rightFree++; + if ((available & (int)Zones.z4) != 0) rightFree++; + + if (leftFree > rightFree) + pick = EMZ_L; + else if (rightFree > leftFree) + pick = EMZ_R; + else + pick = FirstBitFromOrder(emzAvail, new[] { EMZ_L, EMZ_R }); + } + else + { + pick = FirstBitFromOrder(emzAvail, new[] { EMZ_L, EMZ_R }); + } + + AI.SelectPlace(pick); + return pick; + } + if (linkedChoices != 0) + { + pick = FirstBitFromOrder( + linkedChoices, + new[] { (int)Zones.z2, (int)Zones.z1, (int)Zones.z3, (int)Zones.z0, (int)Zones.z4 } + ); + AI.SelectPlace(pick); + return pick; + } + } + int emzOnly = available & EMZ_ALL; + if (emzOnly != 0) + { + int pick = FirstBitFromOrder(emzOnly, new[] { EMZ_L, EMZ_R }); + AI.SelectPlace(pick); + return pick; + } + return PreferSafeSummonZones(available); + } + if (cardId == CardId.CyberseWicckid) + { + int picked = ChooseAndRememberWicckidEmz(available); + if (picked != 0) return picked; + return 0; + } + if (cardId == CardId.TranscodeTalker) + { + int wanted = _forceTranscodeBit != 0 ? _forceTranscodeBit : _wicckidEmzBit; + + if (wanted != 0 && (available & wanted) != 0) + return wanted; + + int anyEmz = available & EMZ_ALL; + if (anyEmz != 0) + return (anyEmz & EMZ_L) != 0 ? EMZ_L : EMZ_R; + + return 0; + } + if (cardId == CardId.MalissQ_RedRansom && _rrSelfSSPlacing) + { + int prefer = (int)Zones.z1 | (int)Zones.z3; + int wmask = GetLinkedMaskFor(GetWicckid()); + int choices = (available & prefer) & ~wmask; + if (choices != 0) + { + int pick = FirstBitFromOrder(choices, new[] { (int)Zones.z1, (int)Zones.z3 }); + AI.SelectPlace(pick); + _rrSelfSSPlacing = false; + return pick; + } + } + if (cardId == CardId.LinkDecoder) + { + ClientCard trans = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.TranscodeTalker)); + int tmask = GetLinkedMaskFor(trans) & 0x1F; + int safe = (available & 0x1F) & ~tmask; + if (safe != 0) + { + int pick = FirstBitFromOrder(safe, new[] { (int)Zones.z2, (int)Zones.z1, (int)Zones.z3, (int)Zones.z0, (int)Zones.z4 }); + AI.SelectPlace(pick); + return pick; + } + } + if (cardId == CardId.FirewallDragon) + { + ClientCard trans = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.TranscodeTalker)); + int underTrans = 0; + if (trans != null) + { + int tmask = GetLinkedMaskFor(trans) & 0x1F; + if ((tmask & (int)Zones.z1) != 0) underTrans |= (int)Zones.z1; + if ((tmask & (int)Zones.z3) != 0) underTrans |= (int)Zones.z3; + } + int choices = (available & underTrans); + if (choices != 0) + { + int pick = FirstBitFromOrder(choices, new[] { (int)Zones.z1, (int)Zones.z3 }); + AI.SelectPlace(pick); + return pick; + } + } + + if (cardId == CardId.TranscodeTalker || + cardId == CardId.AccesscodeTalker || + cardId == CardId.AlliedCodeTalkerIgnister || + cardId == CardId.MalissQ_WhiteBinder || + cardId == CardId.MalissQ_HeartsCrypter) + { + return PreferSafeSummonZones(available); + } + int linked = (GetMyLinkedMMZMask() & available) & 0x1F; + int unlinked = (available & 0x1F) & ~linked; + + int choose; + if (avoidLinkedZones && unlinked != 0) + choose = LowestBit(unlinked); // Not linked zone + else if (linked != 0) + choose = LowestBit(linked); // Default + else + choose = LowestBit(available & 0x1F); + + AI.SelectPlace(choose); + return choose; + } + SelectSTPlace(Card, true); + return base.OnSelectPlace(cardId, player, location, available); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2) + { + bool turnDefense = false; + if (cardData.Attack <= cardData.Defense) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } + int cardAttack = cardData.Attack; + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardAttack); + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + public bool AshBlossomActivate() + { + if (CheckWhetherNegated(true) || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + if (CheckAtAdvantage() && Duel.Turn > 1) + { + return false; + } + } + return DefaultAshBlossomAndJoyousSpring(); + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsMonsterShouldBeDisabledBeforeItUseEffect() && !m.IsDisabled() && Duel.LastChainPlayer != 0) + { + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(m); + return true; + } + } + + ClientCard LastChainCard = Util.GetLastChainCard(); + + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ((this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || (Util.IsChainTarget(Card)) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + List enemyMonsters = Enemy.GetMonsters(); + AI.SelectCard(target); + infiniteImpermanenceList.Add(this_seq); + return true; + } + } + if ((LastChainCard == null || LastChainCard.Controller != 1 || LastChainCard.Location != CardLocation.MonsterZone + || LastChainCard.IsDisabled() || LastChainCard.IsShouldNotBeTarget() || LastChainCard.IsShouldNotBeSpellTrapTarget())) + return false; + + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + if (LastChainCard != null) AI.SelectCard(LastChainCard); + else + { + List enemyMonsters = Enemy.GetMonsters(); + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach (ClientCard card in enemyMonsters) + { + if (card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + { + AI.SelectCard(card); + return true; + } + } + } + return true; + } + + public bool CrossoutDesignatorActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard() != null) + { + int code = Util.GetLastChainCard().Id; + int alias = Util.GetLastChainCard().Alias; + if (alias != 0 && alias - code < 10) code = alias; + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (CheckRemainInDeck(code) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectAnnounceID(code); + currentNegateCardList.AddRange(Enemy.MonsterZone.Where(c => c != null && c.IsFaceup() && c.IsCode(code))); + return true; + } + } + return false; + } + + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) + { + return false; + } + if (Duel.LastChainPlayer == 1) + { + if (Util.GetLastChainCard().IsMonster()) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC) && CheckAtAdvantage() && Duel.Turn > 1) + { + return false; + } + ClientCard graveTarget = Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.GetOriginCode() == code); + if (graveTarget != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(graveTarget); + currentDestroyCardList.Add(graveTarget); + return true; + } + } + + foreach (ClientCard graveCard in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(graveCard) && graveCard.IsMonster()) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + int code = graveCard.Id; + AI.SelectCard(graveCard); + currentDestroyCardList.Add(graveCard); + return true; + } + } + + if (Duel.ChainTargets.Contains(Card)) + { + List enemyMonsters = Enemy.Graveyard.GetMatchingCards(card => card.IsMonster()).ToList(); + if (enemyMonsters.Count > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + int code = enemyMonsters[0].Id; + AI.SelectCard(code); + currentDestroyCardList.Add(enemyMonsters[0]); + return true; + } + } + } + + if (Duel.LastChainPlayer == 1) return false; + List targets = GetDangerousCardinEnemyGrave(true); + if (targets.Count > 0) + { + int code = targets[0].Id; + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + currentDestroyCardList.Add(targets[0]); + return true; + } + + return false; + } + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + List onlyOneSetList = new List { }; + if (onlyOneSetList.Contains(Card.Id) && Bot.HasInSpellZone(Card.Id)) + { + return false; + } + + if ((Card.IsTrap() || Card.HasType(CardType.QuickPlay))) + { + + List avoid_list = new List(); + int setFornfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setFornfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setFornfiniteImpermanence); + return true; + } + else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } + else + { + SelectSTPlace(); + } + if (Card.IsCode(CardId.MalissC_MTP07)) mtp07SetThisTurn = true; + if (Card.IsCode(CardId.MalissC_GWC06)) gwc06SetThisTurn = true; + return true; + } + + return false; + } + + public List GetDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantom) || card.HasSetcode(SetcodeHorus) || card.HasSetcode(SetcodeDarkWorld) || card.HasSetcode(SetcodeSkyStriker))).ToList(); + List dangerMonsterIdList = new List { 99937011, 63542003, 9411399, 28954097, 30680659, 32731036 }; + result.AddRange(Enemy.Graveyard.GetMatchingCards(card => dangerMonsterIdList.Contains(card.Id))); + return result; + } + + public bool CheckWhetherNegated(bool disablecheck = true, bool toFieldCheck = false, CardType type = 0) + { + bool isMonster = type == 0 && Card.IsMonster(); + isMonster |= ((int)type & (int)CardType.Monster) != 0; + bool isSpellOrTrap = type == 0 && (Card.IsSpell() || Card.IsTrap()); + isSpellOrTrap |= (((int)type & (int)CardType.Spell) != 0) || (((int)type & (int)CardType.Trap) != 0); + bool isCounter = ((int)type & (int)CardType.Counter) != 0; + if (isSpellOrTrap && toFieldCheck && CheckSpellWillBeNegate(isCounter)) + return true; + if (DefaultCheckWhetherCardIsNegated(Card)) return true; + if (isMonster && (toFieldCheck || Card.Location == CardLocation.MonsterZone)) + { + if ((toFieldCheck && (((int)type & (int)CardType.Link) != 0)) || Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card)) || Bot.MonsterZone.Any(card => CheckNumber41(card))) return true; + } + if (Enemy.HasInSpellZone(CardId.SkillDrain, true)) return true; + } + if (disablecheck) return (Card.Location == CardLocation.MonsterZone || Card.Location == CardLocation.SpellZone) && Card.IsDisabled() && Card.IsFaceup(); + return false; + } + + public bool CheckNumber41(ClientCard card) + { + return card != null && card.IsFaceup() && card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled(); + } + + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + if (target == null) target = Card; + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap() && (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true))) return true; + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) return true; + } + return false; + } + + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (notToNegateIdList.Contains(lastcard.Id)) return false; + if (DefaultCheckWhetherCardIsNegated(lastcard)) return false; + if (Duel.Turn == 1 && lastcard.IsCode(_CardId.MaxxC)) return false; + + return true; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + ClientCard floodagateCard = Enemy.GetMonsters().Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsFloodgate() && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (floodagateCard != null) return floodagateCard; + + ClientCard dangerCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (dangerCard != null) return dangerCard; + + ClientCard invincibleCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (invincibleCard != null) return invincibleCard; + + ClientCard equippedCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.EquipCards.Count > 0 && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (equippedCard != null) return equippedCard; + + ClientCard enemyExtraMonster = Enemy.MonsterZone.Where(c => c != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && (c.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) || (c.HasType(CardType.Link) && c.LinkCount >= 2)) + && CheckCanBeTargeted(c, canBeTarget, selfType) && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (enemyExtraMonster != null) return enemyExtraMonster; + + if (attack >= 0) + { + if (attack == 0) + attack = Util.GetBestAttack(Bot); + ClientCard betterCard = Enemy.MonsterZone.Where(card => card != null + && card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && CheckCanBeTargeted(card, canBeTarget, selfType) + && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (betterCard != null) return betterCard; + } + return null; + } + + public bool CheckCanBeTargeted(ClientCard card, bool canBeTarget, CardType selfType) + { + if (card == null) return true; + if (canBeTarget) + { + if (card.IsShouldNotBeTarget()) return false; + if (((int)selfType & (int)CardType.Monster) > 0 && card.IsShouldNotBeMonsterTarget()) return false; + if (((int)selfType & (int)CardType.Spell) > 0 && card.IsShouldNotBeSpellTrapTarget()) return false; + if (((int)selfType & (int)CardType.Trap) > 0 && (card.IsShouldNotBeSpellTrapTarget() && !card.IsDisabled())) return false; + } + return true; + } + + public bool CheckShouldNotIgnore(ClientCard cards, bool ignore = false) + { + return !ignore || (!currentDestroyCardList.Contains(cards) && !currentNegateCardList.Contains(cards)); + } + + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreSpells = false, CardType selfType = 0) + { + List resultList = new List(); + + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count > 0) resultList.AddRange(floodagateList); + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).ToList(); + if (problemEnemySpellList.Count > 0) resultList.AddRange(ShuffleList(problemEnemySpellList)); + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count > 0 && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) resultList.AddRange(dangerList); + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count > 0) resultList.AddRange(invincibleList); + + List enemyMonsters = Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c)).OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count > 0) + { + foreach (ClientCard target in enemyMonsters) + { + if ((target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && !resultList.Contains(target) && CheckCanBeTargeted(target, canBeTarget, selfType)) + { + resultList.Add(target); + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !currentDestroyCardList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) && CheckCanBeTargeted(c, canBeTarget, selfType) + && !notToDestroySpellTrap.Contains(c.Id)).ToList(); + if (spells.Count > 0 && !ignoreSpells) resultList.AddRange(ShuffleList(spells)); + + return resultList; + } + + public List GetNormalEnemyTargetList(bool canBeTarget = true, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + List targetList = GetProblematicEnemyCardList(canBeTarget, selfType: selfType); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && enemyPlaceThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && !enemyPlaceThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList())); + + return targetList; + } + + public List GetMonsterListForTargetNegate(bool canBeTarget = false, CardType selfType = 0) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; + } + + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && CheckCanBeTargeted(card, canBeTarget, selfType) + && !currentNegateCardList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && CheckCanBeTargeted(chainingCard, canBeTarget, selfType) && !currentNegateCardList.Contains(chainingCard)) + { + resultList.Add(chainingCard); + } + } + + return resultList; + } + + public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false) + { + ClientCard card = GetProblematicEnemyMonster(0, canBeTarget); + if (card != null) return card; + card = Enemy.MonsterZone.GetHighestAttackMonster(canBeTarget); + if (card != null) return card; + List monsters = Enemy.GetMonsters(); + if (monsters.Count > 0 && !onlyFaceup) return ShuffleCardList(monsters)[0]; + return null; + } + + public ClientCard GetBestEnemySpell(bool onlyFaceup = false, bool canBeTarget = false) + { + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count > 0) + { + return ShuffleCardList(problemEnemySpellList)[0]; + } + + List spells = Enemy.GetSpells().Where(card => !(card.IsFaceup() && card.IsCode(_CardId.EvenlyMatched))).ToList(); + + List faceUpList = spells.Where(ecard => ecard.IsFaceup() && (ecard.HasType(CardType.Continuous) || ecard.HasType(CardType.Field) || ecard.HasType(CardType.Pendulum))).ToList(); + if (faceUpList.Count > 0) + { + return ShuffleCardList(faceUpList)[0]; + } + + if (spells.Count > 0 && !onlyFaceup) + { + return ShuffleCardList(spells)[0]; + } + + return null; + } + + public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false, bool checkGrave = false) + { + ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget); + if (card != null) return card; + + card = GetBestEnemySpell(onlyFaceup, canBeTarget); + if (card != null) return card; + + if (checkGrave && Enemy.Graveyard.Count > 0) + { + List graveMonsterList = Enemy.Graveyard.GetMatchingCards(c => c.IsMonster()).ToList(); + if (graveMonsterList.Count > 0) + { + graveMonsterList.Sort(CardContainer.CompareCardAttack); + graveMonsterList.Reverse(); + return graveMonsterList[0]; + } + return ShuffleCardList(Enemy.Graveyard.ToList())[0]; + } + + return null; + } + + private int LinkVal(ClientCard c) => (c != null && c.HasType(CardType.Link)) ? Math.Max(1, c.LinkCount) : 1; + private static readonly int EMZ_LEFT = 5; + private static readonly int EMZ_RIGHT = 6; + private bool IsInEMZ(ClientCard c) + { + ClientCard[] mz = Bot.MonsterZone; + return (mz.Length > 5 && mz[5] == c) || (mz.Length > 6 && mz[6] == c); + } + + private bool HasFreeEMZ() + { + ClientCard[] mz = Bot.MonsterZone; + bool slot5Free = mz.Length > 5 && mz[5] == null; + bool slot6Free = mz.Length > 6 && mz[6] == null; + return slot5Free || slot6Free; + } + #endregion + + #region work space #1 + public override void OnChainSolved(int chainIndex) + { + ClientCard currentCard = Duel.GetCurrentSolvingChainCard(); + ClientCard solving = Duel.GetCurrentSolvingChainCard(); + bool neg = Duel.IsCurrentSolvingChainNegated(); + if (currentCard != null && !Duel.IsCurrentSolvingChainNegated() && currentCard.Controller == 1) + { + if (currentCard.IsCode(CardId.Lancea)) enemyActivateLancea = true; + if (currentCard.IsCode(_CardId.MaxxC)) enemyActivateMaxxC = true; + if (currentCard.IsCode(CardId.Fuwalos)) enemyActivateFuwalos = true; + if (currentCard.IsCode(_CardId.LockBird)) enemyActivateLockBird = true; + if (currentCard.IsCode(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == currentCard) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + ClientCard last = Duel.GetCurrentSolvingChainCard(); + if (last != null) + { + if (last.IsSpell() && (last.HasType(CardType.Field) || last.HasType(CardType.Continuous) || last.HasType(CardType.Equip))) + _oppJustActivatedPersistentSpell = true; + _prefWindowTTL = Math.Max(_prefWindowTTL, 2); + } + } + if (currentCard != null && currentCard.Controller == 0 && currentCard.IsCode(CardId.SplashMage)) + { + if (Duel.IsCurrentSolvingChainNegated()) + splashNegatedThisTurn = true; + } + } + public override void OnChainEnd() + { + escapeTargetList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + enemyActivateInfiniteImpermanenceFromHand = false; + _oppJustActivatedPersistentSpell = false; + int curMon = Enemy.GetMonsterCount(); + if (curMon > _enemyMonsterCountSnap) _oppJustSummoned = true; + _enemyMonsterCountSnap = curMon; + int curFD = Enemy.SpellZone.Count(c => c != null && c.IsFacedown()); + if (curFD > _enemyFacedownSTSnap) _oppJustSet = true; + _enemyFacedownSTSnap = curFD; + for (int idx = enemyPlaceThisTurn.Count - 1; idx >= 0; idx--) + { + ClientCard checkTarget = enemyPlaceThisTurn[idx]; + if (checkTarget == null || (checkTarget.Location != CardLocation.SpellZone && checkTarget.Location != CardLocation.MonsterZone)) + { + enemyPlaceThisTurn.RemoveAt(idx); + } + } + base.OnChainEnd(); + } + public override void OnNewTurn() + { + if (Duel.Player == 0) + { + myTurnCount++; + } + enemyActivateLancea = false; //added for Maliss + enemyActivateFuwalos = false; //added + enemyActivateMaxxC = false; + enemyActivateLockBird = false; + enemyActivateInfiniteImpermanenceFromHand = false; + if (dimensionShifterCount > 0) dimensionShifterCount--; + infiniteImpermanenceList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + sendToGYThisTurn.Clear(); + activatedCardIdList.Clear(); + enemyPlaceThisTurn.Clear(); + summonThisTurn.Clear(); + + // reset Maliss flags + usedNormalSummon = false; + ssChessyCat = false; + ssDormouse = false; + ssMarchHare = false; + ssWhiteRabbit = false; + ActiveMarchHare = false; + ActiveUnderground = false; + step1Done = false; + step2Done = false; + lastRevivedIdBySplash = 0; + mtp07SetThisTurn = false; + gwc06SetThisTurn = false; + splashNegatedThisTurn = false; + ssRRThisTurn = false; + ssWBThisTurn = false; + ssHCThisTurn = false; + _didSplashToRR = _didRRtoWicckid = _didSummonToWicckidArrow = _didWBFromWicckid = false; + _finishPlanDecided = false; + _preferWicckidArrows = false; + _rrSelfSSPlacing = false; + _forceTranscodeBit = 0; + _oppJustActivatedPersistentSpell = false; + _oppJustSummoned = false; + _oppJustSet = false; + _enemyMonsterCountSnap = Enemy.GetMonsterCount(); + _enemyFacedownSTSnap = Enemy.SpellZone.Count(c => c != null && c.IsFacedown()); + _prefWindowTTL = 0; + fullBoard1 = false; + Allied_End = false; + nsplan = false; + nsBackupplan = false; + NSDorMouse = false; + base.OnNewTurn(); + } + public override bool OnSelectYesNo(long desc) + { + if (desc == Util.GetStringId(CardId.MalissQ_WhiteBinder, 0)) + { + bool anyGY = (Bot.Graveyard.Count > 0) || (Enemy.Graveyard.Count > 0); + return anyGY; + } + return base.OnSelectYesNo(desc); + } + private bool DontSelfNG() { return Duel.LastChainPlayer != 0; } + + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + ClientCard solving = Duel.GetCurrentSolvingChainCard(); + if (cards != null && cards.Count > 0 && solving != null) + { + if (solving.IsCode(CardId.MalissQ_RedRansom)) + { + List searchPool = cards + .Where(c => c != null && + (c.IsCode(CardId.MalissInUnderground) || + c.IsCode(CardId.MalissInTheMirror))) + .ToList(); + + if (searchPool.Count > 0) + { + bool shouldUG = ShouldSearchUnderground(); + int chooseId = 0; + if (shouldUG && searchPool.Any(c => c.IsCode(CardId.MalissInUnderground))) + { + chooseId = CardId.MalissInUnderground; + } + else if (searchPool.Any(c => c.IsCode(CardId.MalissInTheMirror))) + { + chooseId = CardId.MalissInTheMirror; + } + if (chooseId != 0) + { + ClientCard pick = searchPool.First(c => c.IsCode(chooseId)); + return new List { pick }; + } + } + } + if (hint == HintMsg.Set && solving.IsCode(CardId.MalissQ_WhiteBinder)) + { + ClientCard pick = null; + pick = cards.FirstOrDefault(c => c.Id == CardId.MalissC_GWC06 && c.Location == CardLocation.Deck); + if (pick == null) + { + pick = cards.FirstOrDefault(c => c.Id == CardId.MalissC_GWC06); + } + if (pick == null) + { + pick = cards.FirstOrDefault(c => c.Id == CardId.MalissC_MTP07); + } + if (pick != null) + { + if (pick.Id == CardId.MalissC_GWC06) + gwc06SetThisTurn = true; + else if (pick.Id == CardId.MalissC_MTP07) + mtp07SetThisTurn = true; + + return new List { pick }; + } + + } + } + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + #endregion + + + #region work space #2 + private int GetMMZCount() => Bot.MonsterZone.Take(5).Count(c => c != null); + private bool HasFreeMMZ() => GetMMZCount() < 5; + private bool HaveTwoBodies() => Bot.GetMonsterCount() >= 2; + private bool ShouldFastEndToSPLK() => + enemyActivateMaxxC || enemyActivateFuwalos; + private bool Step1Complete() + { + return Bot.HasInMonstersZone(CardId.MalissP_Dormouse) + && Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit); + } + private bool CanStartStep1() + { + if (enemyActivateLancea || ShouldFastEndToSPLK()) return false; + if (HaveTwoBodies()) return false; + if (!HasFreeMMZ()) return false; + bool haveStarterInHand = + Bot.HasInHand(CardId.MalissP_Dormouse) || + Bot.HasInHand(CardId.MalissP_WhiteRabbit) || + Bot.HasInHand(CardId.GoldSarcophagus) || + Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.MalissInUnderground); + return haveStarterInHand; + } + private bool CanContinueStep1() + { + if (enemyActivateLancea) return false; + return !HaveTwoBodies() && HasFreeMMZ(); + } + private int PickMalissTrapToSet() + { + int pref; + if (Duel.Player == 0 && Bot.HasInMonstersZone(CardId.MalissP_Dormouse) && Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit)) + { + pref = CardId.MalissC_GWC06; + } + else if (Duel.Player == 0 && (Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_RedRansom) || Bot.HasInBanished(CardId.MalissQ_RedRansom))) + { + pref = CardId.MalissC_GWC06; + } + else if (Duel.Player == 0 && Bot.HasInHand(CardId.MalissP_MarchHare) && !ActiveMarchHare && !ssWhiteRabbit) + { + pref = CardId.MalissC_GWC06; + } + else if (Duel.Player == 0 && nsBackupplan) + { + pref = CardId.MalissC_GWC06; + } + else if (Duel.Player == 1) + { + pref = CardId.MalissC_MTP07; + } + else + { + pref = CardId.MalissC_MTP07; + } + if (CheckRemainInDeck(pref) > 0) + { + return pref; + } + + return 0; + } + + private bool ActLittleKnight() + { + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.SP_LITTLE_KNIGHT, 0)) + { + List problemCardList = GetProblematicEnemyCardList(true, selfType: CardType.Monster); + problemCardList.AddRange(GetDangerousCardinEnemyGrave(false)); + problemCardList.AddRange(GetNormalEnemyTargetList(true, true, CardType.Monster)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => card.HasType(CardType.Monster)).OrderByDescending(card => card.Attack)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => !card.HasType(CardType.Monster))); + if (problemCardList.Count() > 0) + { + AI.SelectCard(problemCardList); + activatedCardIdList.Add(Card.Id); + return true; + } + } + else if (ActivateDescription == Util.GetStringId(CardId.SP_LITTLE_KNIGHT, 1)) + { + ClientCard selfMonster = null; + foreach (ClientCard target in Bot.GetMonsters()) + { + if (Duel.ChainTargets.Contains(target) && !escapeTargetList.Contains(target)) + { + selfMonster = target; + break; + } + } + if (selfMonster == null) + { + if (Duel.Player == 1) + { + selfMonster = Bot.GetMonsters().Where(card => card.IsAttack()).OrderBy(card => card.Attack).FirstOrDefault(); + if (!Util.IsOneEnemyBetterThanValue(selfMonster.Attack, true)) selfMonster = null; + } + } + if (selfMonster != null) + { + ClientCard nextMonster = null; + List selfTargetList = Bot.GetMonsters().Where(card => card != selfMonster).ToList(); + if (Enemy.GetMonsterCount() == 0 && selfTargetList.Count() > 0) + { + selfTargetList.Sort(CompareUsableAttack); + nextMonster = selfTargetList[0]; + escapeTargetList.Add(nextMonster); + } + if (Enemy.GetMonsterCount() > 0) + { + nextMonster = GetBestEnemyMonster(true, true); + currentDestroyCardList.Add(nextMonster); + } + if (nextMonster != null) + { + AI.SelectCard(selfMonster); + AI.SelectNextCard(nextMonster); + escapeTargetList.Add(selfMonster); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + + return false; + } + public int CompareUsableAttack(ClientCard cardA, ClientCard cardB) + { + if (cardA == null && cardB == null) + return 0; + if (cardA == null) + return -1; + if (cardB == null) + return 1; + int powerA = (cardA.IsDefense() && summonThisTurn.Contains(cardA)) ? 0 : cardA.Attack; + int powerB = (cardB.IsDefense() && summonThisTurn.Contains(cardB)) ? 0 : cardB.Attack; + if (powerA < powerB) + return -1; + if (powerA == powerB) + return CardContainer.CompareCardLevel(cardA, cardB); + return 1; + } + + private bool Step1_Dormouse_NormalSummon() + { + if (!CanStartStep1()) return false; + if (usedNormalSummon) return false; + usedNormalSummon = true; + NSDorMouse = true; + return true; + } + private bool Dormouse_ForMH() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!CanContinueStep1()) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + int pick = 0; + if (goldstart || undergroundstart) + { + pick = (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0) + ? CardId.MalissP_WhiteRabbit + : CardId.MalissP_ChessyCat; + } + else + { + pick = (CheckRemainInDeck(CardId.MalissP_MarchHare) > 0) + ? CardId.MalissP_MarchHare + : (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0) + ? CardId.MalissP_WhiteRabbit + : CardId.MalissP_ChessyCat; + } + if (pick == 0) return false; + AI.SelectCard(pick); + return true; + } + + private bool Step1_WhiteRabbit_SS_FromBanished() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (enemyActivateLancea) return false; + if (Card.Id != CardId.MalissP_WhiteRabbit) return false; + if (Card.Location != CardLocation.Removed) return false; + if (Bot.LifePoints <= 300) + { + return false; + } + ssWhiteRabbit = true; + return true; + } + + private bool Step1_WhiteRabbit_SetTrapOnSummon() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_WhiteRabbit) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + + int trapToSet = PickMalissTrapToSet(); + if (trapToSet == 0) return false; + + if (trapToSet == CardId.MalissC_GWC06) + gwc06SetThisTurn = true; + + if (trapToSet == CardId.MalissC_MTP07) + mtp07SetThisTurn = true; + + AI.SelectCard(trapToSet); + SelectSafeSTZoneAwayFromImperm(); + if (Step1Complete()) step1Done = true; + return true; + } + + private bool Step1_WhiteRabbit_NormalSummon() + { + if (!CanStartStep1()) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse)) return false; + if (usedNormalSummon) return false; + usedNormalSummon = true; + return true; + } + + private bool Dormouse_SS_FromBanished() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_Dormouse) return false; + if (Card.Location != CardLocation.Removed) return false; + if (enemyActivateLancea) return false; + if (Bot.LifePoints <= 300) + { + return false; + } + ssDormouse = true; + return true; + } + private bool ChessyCat_SS_FromBanished() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_ChessyCat) return false; + if (Card.Location != CardLocation.Removed) return false; + if (enemyActivateLancea) return false; + if (Bot.LifePoints <= 300) + { + return false; + } + ssChessyCat = true; + return true; + } + + private bool SummonLittleKnightFast() + { + if (!(Bot.HasInMonstersZone(CardId.LinkSpider)|| Bot.HasInMonstersZone(CardId.Linguriboh))) return false; + if (!HaveTwoBodies()) return false; + + List mats = Bot.GetMonsters() + .Where(c => c != null && c.IsFaceup() && c.HasType(CardType.Effect)) + .OrderBy(c => c.Attack) + .Take(2).ToList(); + + if (mats.Count < 2) return false; + + AI.SelectMaterials(mats); + step1Done = true; + return true; + } + private bool SPEmer() + { + if (Bot.HasInMonstersZone(CardId.FirewallDragon) || + Bot.HasInMonstersZone(CardId.AlliedCodeTalkerIgnister) || + Bot.HasInMonstersZone(CardId.Apollousa) || + Bot.HasInMonstersZone(CardId.AccesscodeTalker)) + { + return false; + } + if (!HaveTwoBodies()) return false; + + List mats = PickLinkMatsMinCount( + targetLink: 2, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.Apollousa, CardId.AlliedCodeTalkerIgnister, CardId.AccesscodeTalker } + ); + if (mats.Count != 2) + { + return false; + } + + AI.SelectMaterials(mats); + return true; + } + + private bool GoldSarc_StartPiece() + { + if (CheckSpellWillBeNegate()) return false; + if (enemyActivateLancea) { return false; } + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit)) { return false; } + int pick = 0; + if (!Bot.HasInMonstersZone(CardId.MalissP_Dormouse) && CheckRemainInDeck(CardId.MalissP_Dormouse) > 0 && !ssDormouse) + { pick = CardId.MalissP_Dormouse; } + else if (!Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit) && CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0 && !ssWhiteRabbit) + { pick = CardId.MalissP_WhiteRabbit; } + else if (madeIt3 && !ssChessyCat) + { pick = CardId.MalissP_ChessyCat; } + else { return false; } + if (pick == 0) return false; + + + AI.SelectCard(pick); + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + goldstart = true; + return true; + } + private bool ExistsForUnderground(int id) + { + return CheckRemainInDeck(id) > 0 + || Bot.HasInHand(id) + || Bot.HasInGraveyard(id); + } + private bool Underground_ActivateStarter() + { + if (enemyActivateLancea) return false; + if (Bot.GetMonsterCount() != 0) return false; + if (step1Done) return false; + + int pick = 0; + if (ExistsForUnderground(CardId.MalissP_Dormouse)) + pick = CardId.MalissP_Dormouse; + else if (ExistsForUnderground(CardId.MalissP_WhiteRabbit)) + pick = CardId.MalissP_WhiteRabbit; + else if (ExistsForUnderground(CardId.MalissP_ChessyCat)) + pick = CardId.MalissP_ChessyCat; + + if (pick == 0) return false; + + AI.SelectYesNo(true); + AI.SelectCard(pick); + ActiveUnderground = true; + undergroundstart = true; + return true; + } + + private bool Terra_GrabUnderground() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (CheckSpellWillBeNegate()) return false; + if (ActiveUnderground) return false; + if (Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInSpellZone(CardId.MalissInUnderground)) + return false; + AI.SelectCard(CardId.MalissInUnderground); + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + return true; + } + private bool HaveBackupOrWizardInHand() + { + return Bot.HasInHand(CardId.BackupIgnister) || Bot.HasInHand(CardId.WizardIgnister); + } + private bool HaveMHInHand() + { + return Bot.HasInHand(CardId.MalissP_MarchHare); + } + private bool IsMalissBody(ClientCard c) + { + return c != null && c.IsFaceup() && c.HasSetcode(SetcodeMaliss) + && c.IsCode(CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare); + } + private bool Emergency_NormalCat() + { + if (Bot.GetMonsterCount() != 0) return false; + if (usedNormalSummon) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) || + Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.GoldSarcophagus)) return false; + if (!HaveMHInHand()) + { + return false; + } + + usedNormalSummon = true; + return true; + } + private bool OneBody_Link1_Linguriboh() + { + if (!HaveBackupOrWizardInHand()) return false; + if (Bot.HasInMonstersZone(CardId.Linguriboh)) return false; + if (Bot.GetMonsterCount() != 1) return false; + return true; + } + private bool OneBody_Link1_Almiraj() + { + if (!HaveBackupOrWizardInHand()) return false; + if (Bot.HasInMonstersZone(CardId.SALAMANGREAT_ALMIRAJ)) return false; + if (Bot.GetMonsterCount() != 1) return false; + return true; + } + private bool OneBody_Backup_SS() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + bool haveLinkAnchor = Bot.HasInMonstersZone(CardId.Linguriboh) || Bot.HasInMonstersZone(CardId.SplashMage); + if (!haveLinkAnchor) return false; + if (Card.Location != CardLocation.Hand) return false; + + avoidLinkedZones = true; + return true; + } + private bool OneBody_Backup_SearchWizard() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!Card.IsCode(CardId.BackupIgnister)) return false; + + if (Bot.Hand.Count == 0) return false; + + bool haveWizard = Bot.HasInHand(CardId.WizardIgnister); + + int searchId = 0; + if (haveWizard && CheckRemainInDeck(CardId.MalissP_MarchHare) > 0 && Bot.Hand.Count > 0) + searchId = CardId.MalissP_MarchHare; + else if (CheckRemainInDeck(CardId.MalissP_Dormouse) > 0 && nsplan && Bot.HasInMonstersZone(CardId.SALAMANGREAT_ALMIRAJ)) + searchId = CardId.MalissP_Dormouse; + else if (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0 && nsBackupplan) + searchId = CardId.MalissP_WhiteRabbit; + else if (!haveWizard && CheckRemainInDeck(CardId.WizardIgnister) > 0 && Bot.Hand.Count > 0) + searchId = CardId.WizardIgnister; + else if (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0) + searchId = CardId.MalissP_WhiteRabbit; + else + return false; + + AI.SelectCard(searchId); + + List hand = Bot.Hand.Where(h => h != null).ToList(); + List candidates = hand.Where(h => h.Id != CardId.WizardIgnister).ToList(); + List discards = new List(hand.Count); + if (searchId == CardId.MalissP_Dormouse) + { + discards.AddRange(candidates.Where(c => c.Id == CardId.MalissP_Dormouse)); + } + Func> OthersExcludingTarget = () => + { + if (searchId == CardId.MalissP_MarchHare) + return candidates.Where(c => c.Id != CardId.MalissP_MarchHare); + return candidates; + }; + List othersExcludingTarget = OthersExcludingTarget().ToList(); + IEnumerable> dupGroups = othersExcludingTarget.GroupBy(c => c.Id) + .Where(g => g.Count() >= 2); + + discards.AddRange(dupGroups.SelectMany(g => g)); + + int[] lowValueSinglesOrder = { CardId.NibiruThePrimalBeing, + CardId.Lancea, + CardId.TERRAFORMING, + CardId.GoldSarcophagus + }; + + foreach (int id in lowValueSinglesOrder) + { + discards.AddRange(othersExcludingTarget + .Where(c => c.Id == id && dupGroups.All(g => g.Key != id))); + } + + HashSet already = new HashSet(discards); + discards.AddRange(othersExcludingTarget.Where(c => !already.Contains(c))); + + discards = discards.Where(c => c != null).Distinct().ToList(); + + if (searchId == CardId.MalissP_Dormouse) + { + AI.SelectNextCard(searchId); + } + if (searchId == CardId.MalissP_WhiteRabbit) + { + AI.SelectNextCard(searchId); + } + else if (discards != null) + { + AI.SelectNextCard(discards); + } + else + { + return false; + } + + avoidLinkedZones = true; + if (Bot.HasInMonstersZone(CardId.Linguriboh)) blockWicckid = true; + if (GetMMZCount() >= 5 && Bot.HasInHand(CardId.WizardIgnister)) { fullBoard1 = true; } + return true; + } + private bool OneBody_Wizard_SS() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!Bot.HasInMonstersZone(CardId.Linguriboh) && !Bot.HasInMonstersZone(CardId.SALAMANGREAT_ALMIRAJ)) return false; + if (Card.Location != CardLocation.Hand) return false; + + ClientCard revive = PickGYCybersePriority(); + if (revive == null) return false; + + avoidLinkedZones = true; + blockWicckid = true; + + AI.SelectCard(revive); + return true; + } + private ClientCard PickGYCybersePriority() + { + ClientCard m = PickGYMalissPriority(); + if (m != null) return m; + + List list = Bot.Graveyard.GetMatchingCards(c => + c != null && c.IsMonster() && c.HasRace(CardRace.Cyberse) && c.Level <= 4).ToList(); + return list.FirstOrDefault(); + } + private ClientCard PickGYMalissPriority() + { + int[] pref = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare + }; + foreach (int id in pref) + { + ClientCard c = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(id) && g.IsMonster()); + if (c != null) return c; + } + return null; + } + private bool TwoCC_NormalSummon() + { + if (usedNormalSummon) return false; + if (Bot.GetMonsterCount() != 0) return false; + + bool haveOtherStarter = + Bot.HasInHand(CardId.MalissP_Dormouse) || + Bot.HasInHand(CardId.MalissP_WhiteRabbit) || + Bot.HasInHand(CardId.GoldSarcophagus) || + Bot.HasInHand(CardId.MalissInUnderground); + + if (haveOtherStarter) return false; + + if (Bot.Hand.GetMatchingCards(c => c != null && c.IsCode(CardId.MalissP_ChessyCat)).Count >= 2) + { + usedNormalSummon = true; + return true; + } + return false; + } + private bool IsMalissCost(ClientCard card) + { + if (card == null) return false; + + if (card.IsCode(CardId.MalissP_ChessyCat)) + return true; + if (card.IsCode(CardId.MalissP_WhiteRabbit) && NSDorMouse && !ssWhiteRabbit) + return true; + if (card.IsCode(CardId.MalissP_WhiteRabbit) && ssWhiteRabbit) + return true; + if (card.IsCode(CardId.MalissP_Dormouse) && ssDormouse) + return true; + if (card.IsCode(CardId.MalissP_MarchHare) && ActiveMarchHare) + return true; + + return false; + } + private bool IsMalissCost2(ClientCard card) + { + if (card == null) return false; + + return + card.IsCode(CardId.MalissP_ChessyCat) || + card.IsCode(CardId.MalissP_WhiteRabbit) || + card.IsCode(CardId.MalissP_MarchHare) || + card.IsCode(CardId.MalissInUnderground); + } + private bool AnyDraw() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_ChessyCat) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (CheckWhetherNegated()) return false; + + ClientCard target = null; + + target = Bot.Hand.FirstOrDefault(c => c != null && c.IsCode(CardId.MalissInTheMirror)); + if (target == null) + { + target = Bot.Hand.FirstOrDefault(IsMalissCost); + } + if (target == null) + { + IGrouping malissDupGroup = Bot.Hand + .Where(IsMalissCost2) + .GroupBy(c => c.Id) + .FirstOrDefault(g => g.Count() >= 2); + + if (malissDupGroup != null) + { + target = malissDupGroup.First(); + } + } + if (target == null) + { + return false; + } + AI.SelectCard(target); + return true; + } + private int PickTB11CostCandidateId() + { + List field = Bot.GetMonsters() + .Where(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeMaliss)) + .ToList(); + + field = field.Where(c => + !(c.IsCode(CardId.MalissP_WhiteRabbit) && ssWhiteRabbit) && + !(c.IsCode(CardId.MalissP_ChessyCat) && ssChessyCat) && + !(c.IsCode(CardId.MalissP_MarchHare) && ssMarchHare) && + !(c.IsCode(CardId.MalissP_Dormouse) && ssDormouse) && + !(c.IsCode(CardId.MalissQ_RedRansom) && ssRRThisTurn) && + !(c.IsCode(CardId.MalissQ_WhiteBinder) && ssWBThisTurn) && + !(c.IsCode(CardId.MalissQ_HeartsCrypter) && ssHCThisTurn) + ).ToList(); + + int[] pref = { + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare, + CardId.MalissP_Dormouse, + CardId.MalissQ_RedRansom, + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_HeartsCrypter + }; + foreach (int id in pref) + if (field.Any(c => c.IsCode(id))) return id; + + return 0; + } + private int PickPFromGYForSplash() + { + if (enemyActivateLancea) + { + int[] pref2 = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare, + CardId.BackupIgnister, + CardId.WizardIgnister + }; + foreach (int id in pref2) + if (Bot.HasInGraveyard(id)) return id; + } + int[] pref = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare + }; + foreach (int id in pref) + if (Bot.HasInGraveyard(id)) return id; + return 0; + } + private bool HaveUndergroundOnHandOrField() + { + return Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInSpellZone(CardId.MalissInUnderground); + } + private bool ShouldSearchUnderground() + { + if (ActiveUnderground) return false; + if (HaveUndergroundOnHandOrField()) return false; + return CheckRemainInDeck(CardId.MalissInUnderground) > 0; + } + private bool Step2_RedRansom_Search() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + int chooseId = 0; + bool shouldUG = ShouldSearchUnderground(); + if (shouldUG) + { + chooseId = CardId.MalissInUnderground; + } + else if (CheckRemainInDeck(CardId.MalissInTheMirror) > 0) + { + chooseId = CardId.MalissInTheMirror; + } + if (chooseId == 0) return false; + + //AI.SelectCard(chooseId); go to onselectcard + step2Done = true; + avoidLinkedZones = false; + coreSetupComplete = true; + return true; + } + private List PickMaterialsForRedRansom() + { + ClientCard splash = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.SplashMage)); + if (splash == null) return new List(); + + ClientCard revived = null; + if (lastRevivedIdBySplash != 0) + revived = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(lastRevivedIdBySplash)); + + if (revived == null) + revived = Bot.MonsterZone.FirstOrDefault(c => + c != null && c.IsFaceup() && c.HasSetcode(SetcodeMaliss) && c != splash && !c.HasType(CardType.Link)); + + if (revived == null) return new List(); + return new List { splash, revived }; + } + private bool Step2_SplashMage_ReviveP() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (step2Done) return false; + + if (!Bot.HasInMonstersZone(CardId.SplashMage)) return false; + + int pick = PickPFromGYForSplash(); + if (pick == 0) return false; + + AI.SelectCard(pick); + lastRevivedIdBySplash = pick; + return true; + } + private bool Step2N_SplashMage_ReviveP() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!Bot.HasInMonstersZone(CardId.SplashMage)) return false; + + int pick = PickPFromGYForSplash(); + if (pick == 0) return false; + + AI.SelectCard(pick); + lastRevivedIdBySplash = pick; + return true; + } + private bool Step2_LinkSummon_RedRansom() + { + if (step2Done) return false; + + List mats = PickMaterialsForRedRansom(); + if (mats.Count != 2) return false; + + if (Util.GetBotAvailZonesFromExtraDeck(mats) == 0) return false; + + AI.SelectMaterials(mats); + madeIt3 = true; + return true; + } + private bool Step2N_LinkSummon_WB() + { + List mats = PickMaterialsForRedRansom(); + if (mats.Count != 2) return false; + + if (Util.GetBotAvailZonesFromExtraDeck(mats) == 0) return false; + + AI.SelectMaterials(mats); + return true; + } + private bool Step2N_LinkSummon_RedRansom() + { + if (!Bot.HasInMonstersZone(CardId.MalissP_MarchHare) || Bot.GetMonsterCount() < 3) + { + return false; + } + madeIt3 = true; + return true; + } + private bool Step2_Fallback_Wizard_AfterSplashNegated() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!splashNegatedThisTurn) return false; + if (Card.Location != CardLocation.Hand) return false; + + ClientCard revive = PickGYMalissPriority(); + if (revive == null) return false; + + avoidLinkedZones = true; + blockWicckid = true; + AI.SelectCard(revive); + return true; + } + private bool Step2_Fallback_Backup_AfterSplashNegated() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!splashNegatedThisTurn) return false; + if (Card.Location != CardLocation.Hand) return false; + + if (GetMMZCount() >= 5) return false; + + int want = !Bot.HasInHand(CardId.WizardIgnister) && CheckRemainInDeck(CardId.WizardIgnister) > 0 + ? CardId.WizardIgnister + : (CheckRemainInDeck(CardId.MalissP_MarchHare) > 0 ? CardId.MalissP_MarchHare : 0); + if (want == 0) return false; + + AI.SelectCard(want); + + ClientCard discard = Bot.Hand.FirstOrDefault(h => h != null && h.Id != CardId.WizardIgnister && h != Card); + if (discard != null) AI.SelectNextCard(discard); + + avoidLinkedZones = true; + blockWicckid = true; + return true; + } + #endregion + + #region FLOW 3: Core → Results (UG / Mirror / TP11 → Make it 3!! → Finishers) + private bool HaveUG() => Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInSpellZone(CardId.MalissInUnderground); + private bool HasSelfSSAvailable(int id) + { + if (id == CardId.MalissP_Dormouse) return !ssDormouse; + if (id == CardId.MalissP_WhiteRabbit) return !ssWhiteRabbit; + if (id == CardId.MalissP_ChessyCat) return !ssChessyCat; + if (id == CardId.MalissP_MarchHare) return !ActiveMarchHare; + return true; + } + private int PickUG_DHG_DormouseFirst() + { + int[] pref = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare + }; + + foreach (int id in pref) + { + if (!HasSelfSSAvailable(id)) continue; + if (ExistsForUnderground(id)) + return id; + } + return 0; + } + + private bool Flow3_UnderGround_Available_SSAnyPawn() + { + if (!step2Done) return false; + if (!HaveUG()) return false; + if (Card.Id != CardId.MalissInUnderground) return false; + if (resultSuccessFlag) return false; + if (GetMMZCount() >= 5) + { + AI.SelectYesNo(false); + return true; + } + if (enemyActivateLancea) + { + AI.SelectYesNo(false); + return true; + } + + int pick = 0; + + if (CheckRemainInDeck(CardId.MalissInTheMirror) > 0 && nsplan) + { + pick = CardId.MalissInTheMirror; + } + else if (CheckRemainInDeck(CardId.MalissInTheMirror) > 0 && NSDorMouse) + { + pick = CardId.MalissInTheMirror; + } + else + { + pick = PickUG_DHG_DormouseFirst(); + } + + if (pick == 0) + { + AI.SelectYesNo(false); + return true; + } + + AI.SelectYesNo(true); + AI.SelectCard(pick); + resultSuccessFlag = true; + return true; + } + private bool Mirror_Banish() + { + if (Card.Location == CardLocation.Removed) + { + ClientCard gy = PickMirrorGYTargetForSearch(); + if (gy == null) return false; + + AI.SelectCard(gy); + + int[] wants = Mirror_SearchOrderForType(false, gy.IsMonster()); + AI.SelectNextCard(wants); + return true; + } + + if (CheckSpellWillBeNegate()) return false; + if (CheckWhetherNegated()) return false; + ClientCard cost = PickMirrorCostCandidate(); + if (cost == null) return false; + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsMonsterShouldBeDisabledBeforeItUseEffect() && !m.IsDisabled() && Duel.LastChainPlayer != 0) + { + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(m); + AI.SelectNextCard(cost); + return true; + } + } + ClientCard LastChainCard = Util.GetLastChainCard(); + if (LastChainCard == null || LastChainCard.Controller != 1 || LastChainCard.Location != CardLocation.MonsterZone + || LastChainCard.IsDisabled() || LastChainCard.IsShouldNotBeTarget() || LastChainCard.IsShouldNotBeSpellTrapTarget()) + return false; + + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + if (LastChainCard != null) AI.SelectCard(LastChainCard); + else + { + List enemyMonsters = Enemy.GetMonsters(); + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach (ClientCard card in enemyMonsters) + { + if (card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + { + AI.SelectCard(card); + } + } + } + AI.SelectNextCard(cost); + return true; + } + private bool Flow3_Link_Accesscode() + { + if (Bot.HasInExtra(CardId.AlliedCodeTalkerIgnister)) return false; + if (BlockAccesscodeOnT1()) return false; + + int[] avoid = { CardId.AlliedCodeTalkerIgnister, CardId.FirewallDragon }; + + List mats = PickLinkMatsMinCount( + targetLink: 4, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 4, + avoidIds: avoid + ); + + if (mats.Count < 2) return false; + AI.SelectMaterials(mats); + return true; + } + private bool Flow3_BackupIgnister_AfterMakeIt3() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!madeIt3) return false; + if (Card.Location != CardLocation.Hand) return false; + + avoidLinkedZones = true; + return true; + } + private bool Flow3_WizardIgnister_AfterMakeIt3() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (!madeIt3) return false; + if (Card.Location != CardLocation.Hand) return false; + + ClientCard revive = PickGYMalissPriority(); + if (revive == null) return false; + + avoidLinkedZones = true; + AI.SelectCard(revive); + return true; + } + private bool RR_SS_FromBanished() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissQ_RedRansom) return false; + if (Card.Location != CardLocation.Removed) return false; + if (GetMMZCount() >= 5) return false; + if (Bot.LifePoints <= 900) + { + return false; + } + + int pickId = 0; + + int[] pawnOrder = { + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_Dormouse + }; + + foreach (int id in pawnOrder) + { + if (CheckRemainInDeck(id) > 0 && PawnSelfSS_AvailableId(id)) + { + pickId = id; + break; + } + } + + + bool canBanish = (pickId != 0); + AI.SelectYesNo(canBanish); + if (canBanish) + { + AI.SelectCard(pickId); + } + _rrSelfSSPlacing = true; + ssRRThisTurn = true; + return true; + } + private bool Wicckid_SearchTuner() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (CheckRemainInDeck(CardId.BackupIgnister) <= 0) return false; + ClientCard cost = PickGYCyberseForWicckidCost_Safe(); + if (cost == null) + { + return false; + } + + AI.SelectCard(cost); + AI.SelectNextCard(CardId.BackupIgnister); + avoidLinkedZones = false; + return true; + } + private bool LinkDecoder_ReviveFromGY() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Grave) { return false; } + if (Allied_End) { return false; } + return true; + } + private bool Transcode_ReviveLink3OrLower() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + + ClientCard prefer = Bot.Graveyard.GetFirstMatchingCard(c => c.IsCode(CardId.CyberseWicckid)) + ?? Bot.Graveyard.GetFirstMatchingCard(c => c.IsCode(CardId.SplashMage)) + ?? Bot.Graveyard.GetFirstMatchingCard(c => c.IsCode(CardId.MalissQ_RedRansom)) + ?? Bot.Graveyard.GetFirstMatchingCard(c => c.IsCode(CardId.MalissQ_WhiteBinder)) + ?? Bot.Graveyard + .GetMatchingCards(c => c.IsMonster() && c.HasType(CardType.Link) && c.LinkCount <= 3 && !c.IsCode(CardId.TranscodeTalker)) + .OrderByDescending(c => c.Attack).FirstOrDefault(); + + if (prefer == null) return false; + AI.SelectCard(prefer); + avoidLinkedZones = false; + return true; + } + private bool Allied_NegateBanish() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (CheckWhetherNegated(true) || !CheckLastChainShouldNegated()) return false; + ClientCard allied = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.AlliedCodeTalkerIgnister)); + if (allied == null || allied.IsDisabled()) return false; + bool haveAnyLink = Bot.GetMonsters().Any(m => m != null && m.HasType(CardType.Link) && !m.IsCode(CardId.AlliedCodeTalkerIgnister)); + if (!haveAnyLink) return false; + + List cheapLinks = new List { + CardId.CyberseWicckid, + CardId.MalissQ_WhiteBinder, + CardId.TranscodeTalker, + CardId.MalissQ_RedRansom + }; + AI.SelectCard(cheapLinks.ToArray()); + return true; + } + private ClientCard GetWicckid() + { + return Bot.MonsterZone.GetFirstMatchingFaceupCard(c => c != null && c.IsCode(CardId.CyberseWicckid)); + } + private int GetLinkedMaskFor(ClientCard link) + { + if (link == null || !link.IsFaceup() || !link.HasType(CardType.Link)) return 0; + return link.GetLinkedZones() & 0x1F; + } + private bool PawnSelfSS_AvailableId(int id) + { + if (id == CardId.MalissP_Dormouse) return !ssDormouse; + if (id == CardId.MalissP_WhiteRabbit) return !ssWhiteRabbit; + if (id == CardId.MalissP_ChessyCat) return !ssChessyCat; + if (id == CardId.MalissP_MarchHare) return !ActiveMarchHare; + return false; + } + private bool QueenSelfSS_AvailableId(int id) + { + if (id == CardId.MalissQ_HeartsCrypter) return !ssHCThisTurn; + if (id == CardId.MalissQ_WhiteBinder) return !ssWBThisTurn; + if (id == CardId.MalissQ_RedRansom) return !ssRRThisTurn; + return false; + } + private bool Dormouse_Banish_Anytime() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Id != CardId.MalissP_Dormouse) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (enemyActivateLancea) return false; + if (!HasFreeMMZ()) return false; + + int pick = 0; + + if (CheckRemainInDeck(CardId.MalissP_WhiteRabbit) > 0 && PawnSelfSS_AvailableId(CardId.MalissP_WhiteRabbit)) + pick = CardId.MalissP_WhiteRabbit; + else if (CheckRemainInDeck(CardId.MalissP_ChessyCat) > 0 && PawnSelfSS_AvailableId(CardId.MalissP_ChessyCat)) + pick = CardId.MalissP_ChessyCat; + + if (pick == 0) return false; + + AI.SelectCard(pick); + return true; + } + private bool IsCyberse(ClientCard c) => c != null && c.HasType(CardType.Monster) && c.HasRace(CardRace.Cyberse); + private bool RR_HOPT_Spent_ThisTurn() => ssRRThisTurn; + private bool RR_CanStillSS_ThisTurn() + { + if (RR_HOPT_Spent_ThisTurn()) return false; + return true; + } + private int Score_WicckidCost(ClientCard c) + { + if (c == null) return int.MinValue; + if (c.IsCode(CardId.MalissQ_RedRansom) && RR_HOPT_Spent_ThisTurn()) return -999; + if (c.IsCode(CardId.MalissQ_RedRansom) && RR_CanStillSS_ThisTurn()) return 1000; + + if (c.IsCode(CardId.Linguriboh) || c.IsCode(CardId.SALAMANGREAT_ALMIRAJ)) return 120; + + if (c.IsCode(CardId.MalissP_Dormouse) && ssDormouse) return 90; + if (c.IsCode(CardId.MalissP_WhiteRabbit) && ssWhiteRabbit) return 70; + if (c.IsCode(CardId.MalissP_ChessyCat) && ssChessyCat) return 60; + if (c.IsCode(CardId.MalissP_MarchHare) && ssMarchHare) return 60; + + if (c.IsCode(CardId.BackupIgnister)) return 20; + if (c.IsCode(CardId.WizardIgnister)) return 20; + return 30; + } + private ClientCard PickGYCyberseForWicckidCost_Safe() + { + List gy = (Bot.Graveyard ?? new List()) + .Where(IsCyberse) + .ToList(); + if (gy.Count == 0) return null; + + ClientCard best = null; + int bestScore = int.MinValue; + foreach (var c in gy) + { + int sc = Score_WicckidCost(c); + if (sc > bestScore) { best = c; bestScore = sc; } + } + return best; + } + private bool BlockAccesscodeOnT1() + { + bool blocked = (Duel.Player == 0 && Duel.Turn == 1); + return blocked; + } + private bool Allied_OnSummonTrigger() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + ClientCard me = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.AlliedCodeTalkerIgnister)); + if (me == null) return false; + + bool has2300 = Bot.Graveyard.Any(c => c != null && c.IsMonster() && c.HasRace(CardRace.Cyberse) && c.Attack == 2300); + if (!has2300) return false; + + List prefer = new List { + CardId.MalissQ_RedRansom, + CardId.MalissQ_WhiteBinder, + CardId.TranscodeTalker + }; + AI.SelectCard(prefer.ToArray()); + return true; + } + private bool ShouldSummonTranscode() + { + bool haveSplashGY = Bot.HasInGraveyard(CardId.SplashMage); + bool haveRR = Bot.HasInMonstersZone(CardId.MalissQ_RedRansom); + bool haveL1 = Bot.HasInMonstersZone(CardId.LinkDecoder) || Bot.HasInGraveyard(CardId.LinkDecoder); + + return haveSplashGY && (haveRR || haveL1); + } + private bool Step_SplashToRR() + { + if (_didSplashToRR) return false; + if (Bot.HasInMonstersZone(CardId.LinkDecoder)) return false; + + if (!Bot.HasInExtra(CardId.MalissQ_RedRansom)) return false; + + List mats = PickLinkMatsMinCount( + targetLink: 2, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.CyberseWicckid } + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + + _didSplashToRR = true; + return true; + } + private bool Step_SplashToWB() + { + if (_didSplashToRR) return false; + + if (!Bot.HasInExtra(CardId.MalissQ_WhiteBinder)) return false; + + List mats = PickLinkMatsMinCount( + targetLink: 2, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.CyberseWicckid } + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + + _didSplashToRR = true; + return true; + } + private bool Step_RRtoWicckid() + { + if(enemyActivateLancea) return false; + if (blockWicckid) return true; + if (!_didSplashToRR || _didRRtoWicckid) return false; + if (!Bot.HasInExtra(CardId.CyberseWicckid)) return false; + + ClientCard rr = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.MalissQ_RedRansom)); + if (rr == null) return false; + ClientCard emzOccupant = Bot.GetMonsters().FirstOrDefault(m => m != null && m != rr && IsInEMZ(m)); + ClientCard buddy = emzOccupant ?? Bot.MonsterZone.FirstOrDefault(m => m != null && m != rr); + if (buddy == null) return false; + bool emzWillBeFree = HasFreeEMZ() || IsInEMZ(rr) || IsInEMZ(buddy); + if (!emzWillBeFree) return false; + AI.SelectCard(CardId.CyberseWicckid); + AI.SelectMaterials(new List { rr, buddy }); + wantLinkedToWicckid = true; + _preferWicckidArrows = true; + _didRRtoWicckid = true; + return true; + } + private bool Step2N_RRtoWicckid() + { + if (enemyActivateLancea) return false; + if (blockWicckid) return false; + ClientCard rr = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.MalissQ_RedRansom)); + if (rr == null) return false; + ClientCard emzOccupant = Bot.GetMonsters().FirstOrDefault(m => m != null && m != rr && IsInEMZ(m)); + ClientCard buddy = emzOccupant ?? Bot.MonsterZone.FirstOrDefault(m => m != null && m != rr); + if (buddy == null) return false; + bool emzWillBeFree = HasFreeEMZ() || IsInEMZ(rr) || IsInEMZ(buddy); + if (!emzWillBeFree) return false; + AI.SelectCard(CardId.CyberseWicckid); + AI.SelectMaterials(new List { rr, buddy }); + wantLinkedToWicckid = true; + _preferWicckidArrows = true; + _didRRtoWicckid = true; + return true; + } + private bool Step_SummonLinkDecoderToWicckid() + { + if (!_didRRtoWicckid || _didSummonToWicckidArrow) return false; + + ClientCard wic = Bot.MonsterZone.GetFirstMatchingCard(m => m != null && m.IsCode(CardId.CyberseWicckid)); + if (wic == null) return false; + + _preferWicckidArrows = true; + avoidLinkedZones = false; + + return true; + } + private bool Step1_SSLinkDecoder() + { + if ((Bot.HasInMonstersZone(CardId.MalissP_Dormouse) || + Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit) || + Bot.HasInMonstersZone(CardId.MalissP_ChessyCat) || + Bot.HasInMonstersZone(CardId.BackupIgnister)) && Bot.GetMonsterCount() == 1 && Bot.HasInHand(CardId.MalissP_MarchHare)) + { + return true; + } + return false; + } + private bool IsMalissMonster(ClientCard c) => c != null && c.IsMonster() && c.HasSetcode(SetcodeMaliss); + private bool CanMakeLinkNWithFlexibleTwo(ClientCard a, ClientCard b, int target) + { + int a1 = 1, aL = (a != null && a.HasType(CardType.Link)) ? Math.Max(1, a.LinkCount) : 1; + int b1 = 1, bL = (b != null && b.HasType(CardType.Link)) ? Math.Max(1, b.LinkCount) : 1; + return (a1 + b1 == target) || (a1 + bL == target) || (aL + b1 == target) || (aL + bL == target); + } + private bool Step_WicckidPlusOneToWB() + { + const int WB_LINK = 3; + if (!Bot.HasInExtra(CardId.MalissQ_WhiteBinder)) return false; + + ClientCard wic = GetWicckid(); + if (wic != null) + { + List partners = Bot.GetMonsters() + .Where(m => m != null && m.IsFaceup() && m != wic && m.HasType(CardType.Effect)) + .ToList(); + + Func okPair = p => (IsMalissMonster(p) || IsMalissMonster(wic)) && CanMakeLinkNWithFlexibleTwo(wic, p, WB_LINK); + + ClientCard p2 = partners.FirstOrDefault(p => !p.IsCode(CardId.MalissQ_RedRansom) && okPair(p)) + ?? partners.FirstOrDefault(p => p.IsCode(CardId.MalissQ_RedRansom) && okPair(p)); + + if (p2 != null) + { + AI.SelectMaterials(new List { wic, p2 }); + _didWBFromWicckid = true; + EnsureFinishPlanAfterWB(); + return true; + } + } + + List mats = PickLinkMatsMinCount( + targetLink: WB_LINK, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 3, + avoidIds: new[] { CardId.Apollousa, CardId.FirewallDragon, CardId.AlliedCodeTalkerIgnister }, + requireMaliss: true + ); + if (mats.Count == 0) return false; + + AI.SelectMaterials(mats); + _didWBFromWicckid = true; + EnsureFinishPlanAfterWB(); + return true; + } + private void EnsureFinishPlanAfterWB() + { + if (_finishPlanDecided) return; + + int free = Bot.MonsterZone.Count(m => m != null && !m.IsCode(CardId.MalissQ_RedRansom)); + bool canFW = Bot.HasInExtra(CardId.FirewallDragon); + bool canHC = Bot.HasInExtra(CardId.MalissQ_HeartsCrypter); + bool canAll = Bot.HasInExtra(CardId.AlliedCodeTalkerIgnister); + bool reachAllied = CanReachAlliedNow(); + + if (canFW && canHC && canAll && free >= 3 && reachAllied) + _finishPlan = FinishPlan.FW_HC_Allied; + else if (canHC && canAll && free >= 2 && reachAllied) + _finishPlan = FinishPlan.HC_Allied; + else + _finishPlan = FinishPlan.AlliedOnly; + + _finishPlanDecided = true; + } + private bool ssFromHandMH() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Hand) return false; + if (GetMMZCount() > 3) return false; + if (enemyActivateLancea) return false; + + if (Duel.Player == 0) + { + List gy = Bot.Graveyard.GetMatchingCards(c => c != null && c.HasSetcode(SetcodeMaliss) && c != Card).ToList(); + + Func, ClientCard> pickP1 = (src) => + { + int[] pref = { + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_HeartsCrypter, + CardId.MalissQ_RedRansom + }; + foreach (int id in pref) + { + ClientCard c = src.FirstOrDefault(x => x.HasType(CardType.Monster) && x.Id == id && QueenSelfSS_AvailableId(x.Id)); + if (c != null) return c; + } + return src.FirstOrDefault(x => x.HasType(CardType.Monster) && QueenSelfSS_AvailableId(x.Id)); + }; + ClientCard pick = pickP1(gy); + if (pick == null) return false; + AI.SelectCard(pick); + ssMarchHare = true; + return DontSelfNG(); + } + else + { + if (Bot.HasInGraveyard(CardId.MalissQ_WhiteBinder) && !ssWBThisTurn) + { + ClientCard Target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissQ_WhiteBinder)); + if (Target == null) return false; + AI.SelectCard(Target); + } + else if (Bot.HasInGraveyard(CardId.MalissQ_HeartsCrypter) && !ssHCThisTurn) + { + ClientCard Target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissQ_HeartsCrypter)); + if (Target == null) return false; + AI.SelectCard(Target); + } + else if (Bot.HasInGraveyard(CardId.MalissQ_RedRansom) && !ssRRThisTurn) + { + ClientCard Target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissQ_RedRansom)); + if (Target == null) return false; + AI.SelectCard(Target); + } + else + { + return false; + } + + ssMarchHare = true; + return DontSelfNG(); + } + } + private bool Step1_MH_FromHand() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Hand) return false; + if (Bot.GetMonsterCount() > 1) return false; + if (enemyActivateLancea) return false; + + if (Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit) && Bot.HasInGraveyard(CardId.MalissC_MTP07)) + { + ClientCard target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissC_MTP07)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + if (Bot.HasInMonstersZone(CardId.LinkDecoder) && Bot.HasInGraveyard(CardId.MalissP_Dormouse)) + { + ClientCard target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissP_Dormouse)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + if (Bot.HasInMonstersZone(CardId.LinkDecoder) && Bot.HasInGraveyard(CardId.MalissP_WhiteRabbit)) + { + ClientCard target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissP_WhiteRabbit)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + if (Bot.HasInMonstersZone(CardId.LinkDecoder) && Bot.HasInGraveyard(CardId.MalissP_ChessyCat)) + { + ClientCard target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissP_ChessyCat)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + if (Bot.HasInMonstersZone(CardId.Linguriboh) && Bot.HasInGraveyard(CardId.MalissP_MarchHare)) + { + ClientCard target = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(CardId.MalissP_MarchHare)); + if (target == null) return false; + AI.SelectCard(target); + ssMarchHare = true; + return DontSelfNG(); + } + + return false; + } + private bool returnFromBanish() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Removed) return false; + + ClientCard mh = Bot.Banished.GetFirstMatchingCard( + c => c.IsFaceup() && c.IsCode(CardId.MalissP_MarchHare)); + + if (mh == null) return false; + + AI.SelectCard(mh); + ActiveMarchHare = true; + return true; + } + private bool WB_OnSummon_BanishGY() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + + const int MAX_PICKS = 3; + List picks = new List(); + + if (Duel.Player == 0) + { + int freeMMZ = Math.Max(0, 5 - GetMMZCount()); + int myPawnCount = Bot.Graveyard + .GetMatchingCards(g => IsMalissPawn(g)) + .Count; + bool canUseOwnPawn = myPawnCount > 1; + + if (!ActiveMarchHare && canUseOwnPawn) + { + ClientCard mh = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_MarchHare)); + if (mh != null) picks.Add(mh); + } + + if (freeMMZ > 0 && !ssRRThisTurn && !Allied_End) + { + ClientCard rr = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissQ_RedRansom)); + if (rr != null && !picks.Contains(rr) && !ShouldSkipBanishing(rr)) + { picks.Add(rr); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssHCThisTurn && !Allied_End) + { + ClientCard hc = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissQ_HeartsCrypter)); + if (hc != null && !picks.Contains(hc) && !ShouldSkipBanishing(hc)) + { picks.Add(hc); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssWhiteRabbit && !Allied_End && canUseOwnPawn) + { + ClientCard wr = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_WhiteRabbit)); + if (wr != null && !picks.Contains(wr) && !ShouldSkipBanishing(wr)) + { picks.Add(wr); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssDormouse && !Allied_End && canUseOwnPawn) + { + ClientCard dm = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_Dormouse)); + if (dm != null && !picks.Contains(dm) && !ShouldSkipBanishing(dm)) + { picks.Add(dm); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssChessyCat && !Allied_End && canUseOwnPawn) + { + ClientCard cc = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_ChessyCat)); + if (cc != null && !picks.Contains(cc) && !ShouldSkipBanishing(cc)) + { picks.Add(cc); freeMMZ--; } + } + + if (freeMMZ >= 0 && canUseOwnPawn) + { + ClientCard target = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_ChessyCat) || g.IsCode(CardId.MalissP_Dormouse)); + picks.Add(target); + } + + picks = picks.Where(c => c != null).Distinct().Take(MAX_PICKS).ToList(); + + List threats = PickEnemyGYThreats(MAX_PICKS - picks.Count); + foreach (var t in threats) + { + if (picks.Count >= MAX_PICKS) break; + if (!picks.Contains(t)) picks.Add(t); + } + } + else + { + int freeMMZ = Math.Max(0, 5 - GetMMZCount()); + + if (!Bot.HasInSpellZone(CardId.MalissC_MTP07) && freeMMZ > 0 && !ssWhiteRabbit) + { + ClientCard wr = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_WhiteRabbit)); + if (wr != null) { picks.Add(wr); freeMMZ--; } + } + + if (picks.Count >= 0 && !Bot.HasInHand(CardId.MalissP_MarchHare) && !ActiveMarchHare) + { + ClientCard mh = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissP_MarchHare)); + if (mh != null) picks.Add(mh); + } + + if (freeMMZ > 0 && !ssRRThisTurn) + { + ClientCard rr = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissQ_RedRansom)); + if (rr != null && !picks.Contains(rr) && !ShouldSkipBanishing(rr)) + { picks.Add(rr); freeMMZ--; } + } + + if (freeMMZ > 0 && !ssHCThisTurn) + { + ClientCard hc = Bot.Graveyard.GetFirstMatchingCard(g => g.IsCode(CardId.MalissQ_HeartsCrypter)); + if (hc != null && !picks.Contains(hc) && !ShouldSkipBanishing(hc)) + { picks.Add(hc); freeMMZ--; } + } + + List threats = PickEnemyGYThreats(MAX_PICKS - picks.Count); + foreach (var t in threats) + { + if (picks.Count >= MAX_PICKS) break; + if (!picks.Contains(t)) picks.Add(t); + } + } + + if (picks.Count == 0) return false; + + if (picks.Count > MAX_PICKS) picks = picks.Take(MAX_PICKS).ToList(); + if (picks.Count < MAX_PICKS) + { + List more = PickEnemyGYThreats(MAX_PICKS - picks.Count); + foreach (var t in more) + if (!picks.Contains(t)) picks.Add(t); + } + AI.SelectCard(picks); + return true; + } + private bool IsMalissPawn(ClientCard c) + { + return c != null && ( + c.IsCode(CardId.MalissP_ChessyCat) || + c.IsCode(CardId.MalissP_WhiteRabbit) || + c.IsCode(CardId.MalissP_Dormouse) || + c.IsCode(CardId.MalissP_MarchHare) + ); + } + private List PickEnemyGYThreats(int need) + { + List result = new List(); + if (need <= 0) return result; + + List danger = GetDangerousCardinEnemyGrave(false); + foreach (var c in danger) + { + if (result.Count >= need) break; + if (!result.Contains(c)) result.Add(c); + } + + if (result.Count < need) + { + for (int i = Enemy.Graveyard.Count - 1; i >= 0 && result.Count < need; i--) + { + ClientCard c = Enemy.Graveyard[i]; + if (c == null) continue; + if (!result.Contains(c)) result.Add(c); + } + } + + if (result.Count < need) + { + foreach (var c in Enemy.Graveyard) + { + if (result.Count >= need) break; + if (!result.Contains(c)) result.Add(c); + } + } + + return result; + } + private bool WB_OnBanished_SelfSS() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Removed) return false; + if (GetMMZCount() >= 5) return false; + if (Bot.LifePoints <= 900) + { + return false; + } + AI.SelectYesNo(true); + ssWBThisTurn = true; + return true; + } + private int PickMalissTrapForWB() + { + if (CheckRemainInDeck(CardId.MalissC_GWC06) > 0 || Bot.HasInGraveyard(CardId.MalissC_GWC06)) + return CardId.MalissC_GWC06; + + if (CheckRemainInDeck(CardId.MalissC_MTP07) > 0 || Bot.HasInGraveyard(CardId.MalissC_MTP07)) + return CardId.MalissC_MTP07; + + return 0; + } + private bool WB_SetMalissTrap() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (ActivateDescription != Util.GetStringId(CardId.MalissQ_WhiteBinder, 1)) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + + + SelectSafeSTZoneAwayFromImperm(); + return true; + } + private List PickLinkMatsMinCount(int targetLink, + Func isEligible, + int minCount, + int maxCount, + IEnumerable avoidIds = null, + bool requireMaliss = false) + { + List all = Bot.GetMonsters() + .Where(m => m != null && m.IsFaceup() && isEligible(m)) + .ToList(); + if (all.Count < minCount) return new List(); + + HashSet avoid = new HashSet(avoidIds ?? Enumerable.Empty()); + + Func, List> OrderForFirst = (src) => src.OrderByDescending(LinkValOf) + .ThenBy(m => avoid.Contains(m.Id) ? 1 : 0) + .ThenBy(m => m.Attack) + .ToList(); + + Func, List> OrderForLater = (src) => src.OrderBy(m => IsOneVal(m) ? 0 : 1) + .ThenBy(m => m.HasType(CardType.Link) ? (Math.Max(1, m.LinkCount) == 1 ? 0 : 1) : -1) + .ThenBy(LinkValOf) + .ThenBy(m => avoid.Contains(m.Id) ? 1 : 0) + .ThenBy(m => m.Attack) + .ToList(); + + List poolPreferred = all.Where(m => !avoid.Contains(m.Id)).ToList(); + List poolFallback = all.ToList(); + + int firstMaxAllowed = targetLink - Math.Max(0, minCount - 1); + + Func, List> TryPick = (pool) => + { + List chosen = new List(); + Func, int, bool> Dfs = null; + Dfs = (avail, sum) => + { + if (sum > targetLink || chosen.Count > maxCount) return false; + + if (chosen.Count >= minCount && sum == targetLink) + { + if (requireMaliss && !chosen.Any(IsMalissBody)) return false; + if (Util.GetBotAvailZonesFromExtraDeck(chosen) == 0) return false; + return true; + } + + int need = Math.Max(0, minCount - chosen.Count); + if (sum + need > targetLink) return false; + + List ordered = (chosen.Count == 0) + ? OrderForFirst(avail) + : OrderForLater(avail); + + for (int i = 0; i < ordered.Count; i++) + { + ClientCard m = ordered[i]; + List nextAvail = ordered.Where((x, idx) => idx != i).ToList(); + int lv = LinkValOf(m); + + int[] values = (chosen.Count == 0) + ? (lv > 1 ? new[] { Math.Min(lv, firstMaxAllowed), 1 } : new[] { 1 }) + : (lv > 1 ? new[] { 1, lv } : new[] { 1 }); + + foreach (int v in values.Distinct()) + { + int newSum = sum + v; + if (newSum > targetLink) continue; + + int remMin = Math.Max(0, minCount - (chosen.Count + 1)); + if (newSum + remMin > targetLink) continue; + + chosen.Add(m); + if (Dfs(nextAvail, newSum)) return true; + chosen.RemoveAt(chosen.Count - 1); + } + } + return false; + }; + + if (Dfs(OrderForFirst(pool), 0)) return chosen; + return null; + }; + + List pick = TryPick(poolPreferred); + if (pick != null && pick.Count > 0) return pick; + + pick = TryPick(poolFallback); + return pick ?? new List(); + } + private bool Link_Apo() + { + if (!(Bot.HasInMonstersZone(CardId.MalissQ_RedRansom) && + Bot.HasInMonstersZone(CardId.LinkDecoder))) return false; + + + List mats = PickLinkMatsMinCount( + targetLink: 4, + isEligible: m => m.HasType(CardType.Monster), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.TranscodeTalker, CardId.AlliedCodeTalkerIgnister, CardId.AccesscodeTalker } + ); + if (mats.Count == 0) return false; + AI.SelectMaterials(mats); + blockWicckid = true; + return true; + } + private bool Flow3_Link_Firewall() + { + if (Bot.HasInMonstersZone(CardId.MalissQ_HeartsCrypter) && Bot.HasInMonstersZone(CardId.LinkDecoder) && Bot.HasInMonstersZone(CardId.CyberseWicckid)) + { + List mats = PickLinkMatsMinCount( + targetLink: 4, + isEligible: m => m.HasType(CardType.Monster), + minCount: 2, + maxCount: 2, + avoidIds: new[] { CardId.CyberseWicckid, CardId.Apollousa, CardId.TranscodeTalker, CardId.AlliedCodeTalkerIgnister, CardId.AccesscodeTalker } + ); + if (mats.Count == 0) return false; + AI.SelectMaterials(mats); + return true; + } + return false; + } + private bool Step_LinkSummon_HeartsCrypter() + { + if(Bot.HasInMonstersZone(CardId.MalissQ_WhiteBinder) && Bot.HasInMonstersZone(CardId.MalissQ_RedRansom) && + Bot.HasInMonstersZone(CardId.Apollousa) && Bot.GetMonsterCount() < 5) return false; + List cand = Bot.GetMonsters() + .Where(c => c != null && c.IsFaceup() && c.HasType(CardType.Effect)) + .ToList(); + if (cand.Count < 3) return false; + + HashSet avoid = new HashSet { CardId.CyberseWicckid, CardId.Apollousa, CardId.AlliedCodeTalkerIgnister, CardId.AccesscodeTalker, CardId.FirewallDragon, CardId.TranscodeTalker }; + + List ordered = cand + .OrderBy(m => avoid.Contains(m.Id) ? 2 : 0) + .ThenBy(m => m.HasType(CardType.Link) ? 1 : 0) + .ThenBy(m => m.Attack) + .ToList(); + + List mats = ordered.Take(3).ToList(); + if (!mats.Any(IsMaliss)) + { + ClientCard maliss = ordered.FirstOrDefault(IsMaliss); + if (maliss == null) return false; + mats[2] = maliss; + } + + if (Util.GetBotAvailZonesFromExtraDeck(mats) == 0) return false; + AI.SelectMaterials(mats); + return true; + } + private bool HC_Quick_ReturnBanished_AndBanishField() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (ActivateDescription != Util.GetStringId(CardId.MalissQ_HeartsCrypter, 0)) return false; + if (Card.Location != CardLocation.MonsterZone) return false; + if (CheckWhetherNegated()) return false; + + RefreshNoChainWindows(); + + List banishedMaliss = GetBanishedMaliss(); + if (banishedMaliss.Count == 0) return false; + bool haveReturn = banishedMaliss.Count > 0; + + bool mustNow = GetProblematicEnemyCardList( + canBeTarget: true, + selfType: CardType.Monster | CardType.Spell | CardType.Trap + ).Count > 0; + + if (Duel.Player == 0) + { + if (!haveReturn && !mustNow) return false; + } + else + { + if (!mustNow && !IsPreferredRemovalTiming()) return false; + } + + if (!haveReturn) return false; + ClientCard ret = PickBanishedMalissForHC(banishedMaliss); + if (ret == null) return false; + + List fieldTargets = GetProblematicEnemyCardList(true, selfType: CardType.Monster | CardType.Spell | CardType.Trap); + if (fieldTargets.Count == 0) + { + ClientCard any = GetBestEnemyCard(false, true); + if (any != null) fieldTargets.Add(any); + } + if (fieldTargets.Count == 0) return false; + + AI.SelectCard(ret); + AI.SelectNextCard(fieldTargets); + ConsumePreferredWindow(); + return DontSelfNG(); + } + private List GetBanishedMaliss() + { + return Bot.Banished.GetMatchingCards(c => + c != null && c.IsFaceup() && c.HasSetcode(SetcodeMaliss)).ToList(); + } + private ClientCard PickBanishedMalissForHC(List cand) + { + cand = cand + .Where(c => !c.IsCode(CardId.MalissQ_WhiteBinder) && !c.IsCode(CardId.MalissQ_RedRansom)) + .ToList(); + if (cand.Count == 0) return null; + + return cand.OrderByDescending(ScoreForBanishedMaliss).First(); + } + private bool HC_OnBanished_SpecialSummon() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Card.Location != CardLocation.Removed) return false; + + if (GetMMZCount() >= 5) + { + return false; + } + + if (Bot.LifePoints <= 900) + { + return false; + } + + AI.SelectYesNo(true); + ssHCThisTurn = true; + return true; + } + private bool HasMalissLinkFaceup() + { + return Bot.GetMonsters().Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeMaliss) && c.HasType(CardType.Link)); + } + private bool CanReachAlliedNow() + { + List mats = PickLinkMatsMinCount( + targetLink: 5, + isEligible: m => m.HasType(CardType.Effect), + minCount: 3, + maxCount: 5, + avoidIds: new[] { CardId.Apollousa, CardId.FirewallDragon, CardId.AccesscodeTalker } + ); + return mats.Count > 0; + } + private bool Flow3_Link_Allied() + { + if (!(Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_RedRansom) && Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_WhiteBinder))) return false; + List mats = PickLinkMatsMinCount( + targetLink: 5, + isEligible: m => m.HasType(CardType.Effect), + minCount: 3, + maxCount: 5, + avoidIds: new[] { CardId.Apollousa, CardId.FirewallDragon, CardId.AccesscodeTalker } + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + Allied_End = true; + return true; + } + private bool Emer_Allied() + { + if(!enemyActivateLancea) return false; + if (!(Bot.HasInMonstersZone(CardId.TranscodeTalker) && Bot.HasInMonstersZone(CardId.SplashMage))) return false; + List mats = PickLinkMatsMinCount( + targetLink: 5, + isEligible: m => m.HasType(CardType.Effect), + minCount: 3, + maxCount: 3 + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + Allied_End = true; + return true; + } + private bool Emer_Allied2() + { + List myMonsters = Bot.GetMonsters().Where(m => m != null).ToList(); + if (myMonsters.Count != 3) return false; + + List link3List = myMonsters + .Where(m => m.HasType(CardType.Link) && m.LinkCount == 3) + .ToList(); + + if (link3List.Count != 1) return false; + + bool haveKeyInGY = + Bot.HasInGraveyard(CardId.MalissQ_RedRansom) || + Bot.HasInGraveyard(CardId.MalissQ_WhiteBinder) || + Bot.HasInGraveyard(CardId.TranscodeTalker); + + if (!haveKeyInGY) return false; + + List mats = PickLinkMatsMinCount( + targetLink: 5, + isEligible: m => m.HasType(CardType.Effect), + minCount: 3, + maxCount: 3 + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + Allied_End = true; + return true; + } + private ClientCard FindGWC06TargetByOrder(params int[] ids) + { + foreach (int id in ids) + { + ClientCard gy = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(id)); + if (gy != null) return gy; + + ClientCard ban = Bot.Banished.GetFirstMatchingCard(c => c != null && c.IsCode(id) && c.IsFaceup()); + if (ban != null) return ban; + } + return null; + } + private int PickGWC06CostCandidateId() + { + if (Bot.HasInMonstersZone(CardId.MalissQ_WhiteBinder) && !ssWBThisTurn) + return CardId.MalissQ_WhiteBinder; + if (Bot.HasInMonstersZone(CardId.MalissQ_RedRansom) && !ssRRThisTurn) + return CardId.MalissQ_RedRansom; + + int card = PickTB11CostCandidateId(); + if (card != 0) return card; + + return 0; + } + private ClientCard PickGWC06TargetExtend() + { + if (Duel.Turn > 2) + { + return FindGWC06TargetByOrder( + CardId.MalissQ_HeartsCrypter, + CardId.MalissQ_RedRansom, + CardId.MalissQ_WhiteBinder + ); + } + else { + return FindGWC06TargetByOrder( + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_RedRansom, + CardId.MalissQ_HeartsCrypter, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_Dormouse, + CardId.MalissP_ChessyCat, + CardId.MalissP_MarchHare + ); + } + } + private bool GWC06_MyTurn_Extend() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (Duel.Player == 0 && + GetMMZCount() >= 4 && + Bot.HasInMonstersZone(CardId.FirewallDragon) && + (Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_RedRansom) || + Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_WhiteBinder) || + Bot.HasInMonstersZoneOrInGraveyard(CardId.MalissQ_HeartsCrypter))) return false; + if (Duel.Player != 0) return false; + if (!(Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2)) return false; + if (CheckSpellWillBeNegate()) return false; + if (GetMMZCount() >= 5) return false; + if (!Bot.HasInMonstersZone(CardId.MalissQ_WhiteBinder) && !(Bot.HasInMonstersZone(CardId.MalissQ_RedRansom) && Bot.GetMonsterCount() == 1)) return false; + ClientCard target = PickGWC06TargetExtend(); + if (target == null) return false; + if (gwc06SetThisTurn) + { + int costId = PickGWC06CostCandidateId(); + if (costId == 0) return false; + AI.SelectCard(costId); + AI.SelectNextCard(target); + return DontSelfNG(); + } + AI.SelectCard(target); + return DontSelfNG(); + } + private bool GWC06_OppTurn_ReviveWB_HC() + { + if (Duel.Player != 1) return false; + if (!Bot.HasInSpellZone(CardId.MalissC_GWC06)) return false; + if (CheckSpellWillBeNegate()) return false; + if (GetMMZCount() >= 5) return false; + + ClientCard target = FindGWC06TargetByOrder( + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_HeartsCrypter + ); + if (target == null) return false; + + if (gwc06SetThisTurn) + { + int costId = PickGWC06CostCandidateId(); + if (costId == 0) return false; + AI.SelectCard(costId); + AI.SelectNextCard(target); + return DontSelfNG(); + } + + AI.SelectCard(target); + return DontSelfNG(); + } + private static int FirstBit(int mask) + { + for (int i = 0; i < 32; i++) + { + int b = 1 << i; + if ((mask & b) != 0) return b; + } + return 0; + } + private static int FirstBitFromOrder(int mask, int[] order) + { + foreach (int b in order) + if ((mask & b) != 0) return b; + return FirstBit(mask); + } + private int PreferSafeSummonZones(int available) + { + int MAIN_MASK = + (int)Zones.z0 | + (int)Zones.z1 | + (int)Zones.z2 | + (int)Zones.z3 | + (int)Zones.z4; + + int emzMask = available & ~MAIN_MASK; + + if (emzMask != 0) + return FirstBit(emzMask); + + int enemyPointed = 0; + try + { + enemyPointed = Enemy.GetLinkedZones(); + } + catch { } + + int safeMain = (available & MAIN_MASK) & ~enemyPointed; + + if (safeMain != 0) + { + return FirstBitFromOrder( + safeMain, + new[] { (int)Zones.z2, (int)Zones.z1, (int)Zones.z3, (int)Zones.z0, (int)Zones.z4 } + ); + } + + return FirstBit(available); + } + private int PickMTP07CostCandidateId() + { + return PickTB11CostCandidateId(); + } + private ClientCard PickMTP07EnemyRemovalTarget() + { + List list = GetProblematicEnemyCardList(canBeTarget: true, ignoreSpells: false, selfType: CardType.Trap); + if (list.Count > 0) return list[0]; + + ClientCard m = GetBestEnemyMonster(onlyFaceup: false, canBeTarget: true); + if (m != null) return m; + + ClientCard s = GetBestEnemySpell(onlyFaceup: false, canBeTarget: true); + if (s != null) return s; + + return Enemy.GetMonsters().FirstOrDefault(c => c != null) + ?? Enemy.GetSpells().FirstOrDefault(c => c != null); + } + private bool MTP07_ForMH() + { + if (!(Bot.GetMonsterCount() == 1 && Bot.HasInMonstersZone(CardId.MalissP_WhiteRabbit))) return false; + if (CheckSpellWillBeNegate()) return false; + if (CheckWhetherNegated()) return false; + + int searchId = PickMTP07SearchId(); + if (searchId == 0) return false; + + if (mtp07SetThisTurn) + { + int costId = PickMTP07CostCandidateId(); + if (costId == 0) return false; + AI.SelectCard(costId); + } + AI.SelectNextCard(searchId); + return DontSelfNG(); + } + private bool MTP07_OppTurn_RemoveEnemyOnly() + { + if (Duel.Player != 1) return false; + if (!HasMalissLinkFaceup()) return false; + if (CheckSpellWillBeNegate()) return false; + if (CheckWhetherNegated()) return false; + List urgent = GetProblematicEnemyCardList(canBeTarget: true, ignoreSpells: false, selfType: CardType.Trap); + if (urgent.Count == 0 && !IsPreferredRemovalTiming()) return false; + bool preBattle = Duel.Phase == DuelPhase.Main1 && Enemy.GetMonsterCount() > 0; + if (urgent.Count == 0 && !(IsPreferredRemovalTiming() || preBattle)) return false; + int searchId = PickMTP07SearchId(); + if (searchId == 0) return false; + ClientCard target = null; + if (urgent.Count > 0) + { + target = urgent[0]; + } + else if (preBattle) + { + target = Enemy.MonsterZone + .Where(c => c != null && c.IsFaceup()) + .OrderByDescending(c => c.Attack) + .FirstOrDefault(); + } + else + { + target = PickMTP07EnemyRemovalTarget(); + } + if (target == null) return false; + if (mtp07SetThisTurn) + { + int costId = PickMTP07CostCandidateId(); + if (costId == 0) return false; + AI.SelectCard(costId); + AI.SelectNextCard(searchId); + AI.SelectNextCard(target); + ConsumePreferredWindow(); + return DontSelfNG(); + } + AI.SelectCard(searchId); + AI.SelectNextCard(target); + ConsumePreferredWindow(); + return DontSelfNG(); + } + private bool Emergency_NS() + { + if (usedNormalSummon) return false; + if (Bot.GetMonsterCount() != 0) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) + || Bot.HasInHand(CardId.MalissP_ChessyCat) + || Bot.HasInHand(CardId.GoldSarcophagus) || Bot.HasInHand(CardId.MalissInUnderground)) return false; + if (!Bot.HasInHand(CardId.BackupIgnister)) return false; + usedNormalSummon = true; + nsplan = true; + return true; + } + private bool IsWicInEMZ(ClientCard wic) + { + return wic != null + && wic.Location == CardLocation.MonsterZone + && (wic.Sequence == EMZ_LEFT || wic.Sequence == EMZ_RIGHT); + } + private IEnumerable GetWicDownSeq(ClientCard wic) + { + if (!IsWicInEMZ(wic)) yield break; + if (wic.Sequence == EMZ_LEFT) { yield return 1; yield return 2; } + if (wic.Sequence == EMZ_RIGHT) { yield return 3; yield return 4; } + } + private bool IsUnderWic(ClientCard wic, ClientCard m) + { + if (wic == null || m == null) return false; + if (m.Controller != wic.Controller) return false; + if (m.Location != CardLocation.MonsterZone) return false; + if (m.Sequence < 0 || m.Sequence > 4) return false; + return GetWicDownSeq(wic).Contains(m.Sequence); + } + private ClientCard PickUnderlingForTranscode(ClientCard wic, System.Collections.Generic.IList pool) + { + if (!IsWicInEMZ(wic)) return null; + List underlings = pool.Where(x => x != null && x != wic && x.IsFaceup() + && x.Location == CardLocation.MonsterZone + && x.Controller == wic.Controller + && x.Sequence >= 0 && x.Sequence <= 4 + && IsUnderWic(wic, x) + && (x.HasType(CardType.Link) || x.HasType(CardType.Effect)) + && !x.HasType(CardType.Token)) + .ToList(); + + if (underlings.Count == 0) return null; + + ClientCard preferLD = underlings.FirstOrDefault(x => x.IsCode(CardId.LinkDecoder)); + if (preferLD != null) return preferLD; + return underlings + .OrderByDescending(x => x.HasType(CardType.Link) ? 2 : 1) + .ThenByDescending(x => x.Attack) + .FirstOrDefault(); + } + private static readonly int[] PreferCenterMainSeq = new[] { 2, 1, 3, 0, 4 }; + + private bool SummonTranscode() + { + if (!enemyActivateLancea) return false; + ClientCard wic = Bot.GetMonsters().FirstOrDefault( + x => x != null && x.IsFaceup() && x.IsCode(CardId.CyberseWicckid)); + + if (!IsWicInEMZ(wic)) return false; + + ClientCard under = PickUnderlingForTranscode(wic, Bot.GetMonsters()); + if (under == null) return false; + + SelectLinkMaterialsPair(wic, under); + + int emz = EmzBitFor(wic); + if (emz != 0) AI.SelectPlace(emz); + + return true; + } + private void SelectLinkMaterialsPair(ClientCard a, ClientCard b) + { + try + { + List mats = new List { a, b }; + AI.SelectMaterials(mats); + return; + } + catch { } + + AI.SelectCard(a); + AI.SelectNextCard(b); + } + private int EmzBitFor(ClientCard link) + { + if (link == null || link.Location != CardLocation.MonsterZone) return 0; + if (link.Sequence == EMZ_LEFT) return 1 << EMZ_LEFT; // 1<<5 + if (link.Sequence == EMZ_RIGHT) return 1 << EMZ_RIGHT; // 1<<6 + return 0; + } + int ChooseAndRememberWicckidEmz(int available) + { + int emzAvail = available & EMZ_ALL; + if (emzAvail == 0) return 0; + + int best = 0; + int bestScore = int.MinValue; + + foreach (int emz in new[] { EMZ_L, EMZ_R }) + { + if ((emzAvail & emz) == 0) continue; + + int score = 0; + int down = DownBitOfEmz(emz); + + if (down == MZ1 && IsMainFreeSeq(1)) score += 10; + if (down == MZ3 && IsMainFreeSeq(3)) score += 10; + + if (score > bestScore) + { + bestScore = score; + best = emz; + } + } + + if (best == 0) + best = (emzAvail & EMZ_L) != 0 ? EMZ_L : EMZ_R; + + _wicckidEmzBit = best; + return best; + } + bool IsMainFreeSeq(int seq) + { + var ms = Bot.MonsterZone.GetMonsters(); + bool occupied = ms.Any(m => m != null && m.Controller == 0 && m.Sequence == seq); + return !occupied; + } + int DownBitOfEmz(int emzBit) + { + if (emzBit == EMZ_L) return MZ1; + if (emzBit == EMZ_R) return MZ3; + return 0; + } + private bool IsPreferredRemovalTiming() + { + if (Duel.Player != 1) return false; + if (_prefWindowTTL > 0) return true; + if (_oppJustActivatedPersistentSpell || _oppJustSummoned || _oppJustSet) return true; + + return false; + } + private void ConsumePreferredWindow() + { + _prefWindowTTL = 0; + _oppJustActivatedPersistentSpell = false; + _oppJustSummoned = false; + _oppJustSet = false; + } + private void RefreshNoChainWindows() + { + bool oppMain = (Duel.Player == 1) && (Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2); + + int curMon = Enemy.GetMonsterCount(); + if (curMon > _enemyMonsterCountSnap) + { + _oppJustSummoned = true; + if (oppMain) _prefWindowTTL = Math.Max(_prefWindowTTL, 2); + } + _enemyMonsterCountSnap = curMon; + + int curFD = Enemy.SpellZone.Count(c => c != null && c.IsFacedown()); + if (curFD > _enemyFacedownSTSnap) + { + _oppJustSet = true; + if (oppMain) _prefWindowTTL = Math.Max(_prefWindowTTL, 2); + } + _enemyFacedownSTSnap = curFD; + + if (oppMain) + { + if (_prefWindowTTL > 0) _prefWindowTTL--; + if (_prefWindowTTL == 0) + { + _oppJustActivatedPersistentSpell = false; + _oppJustSummoned = false; + _oppJustSet = false; + } + } + else + { + _prefWindowTTL = 0; + _oppJustActivatedPersistentSpell = false; + _oppJustSummoned = false; + _oppJustSet = false; + } + } + private bool FirewallBounce_OnOppSummon() + { + if (DefaultCheckWhetherCardIdIsNegated(Card.Id)) return false; + if (ActivateDescription != Util.GetStringId(CardId.FirewallDragon, 0)) + return false; + + if (Duel.LastSummonPlayer != 1) + return false; + + List picks = new List(); + + List negateList = GetMonsterListForTargetNegate(); + if (negateList != null) + { + foreach (var c in negateList) + { + if (c == null) continue; + if (c.Controller != 1) continue; + if (!c.IsMonster()) continue; + if (!c.IsFaceup()) continue; + if (!picks.Contains(c)) + picks.Add(c); + } + } + foreach (var m in Enemy.GetMonsters().OrderByDescending(x => x.Attack)) + { + if (m == null) continue; + if (!m.IsMonster()) continue; + if (!m.IsFaceup()) continue; + if (!picks.Contains(m)) + picks.Add(m); + } + + if (picks.Count == 0) + return false; + + AI.SelectCard(picks); + return true; + } + private bool Accesscode_OnSummon_AtkUp() + { + List list = new List(); + int[] prefer = { + CardId.MalissQ_HeartsCrypter, + CardId.MalissQ_WhiteBinder, + CardId.MalissQ_RedRansom, + CardId.TranscodeTalker + }; + foreach (int id in prefer) + { + ClientCard hit = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(id)); + if (hit != null) list.Add(hit); + } + + AI.SelectCard(list); + return true; + } + + private bool Accesscode_Destroy_Ignition() + { + if (ActivateDescription != Util.GetStringId(CardId.AccesscodeTalker, 1)) + return false; + + if (Enemy.GetFieldCount() == 0) + return false; + + IEnumerable costPool = + (Bot.GetGraveyardMonsters() ?? Enumerable.Empty()) + .Where(c => c.HasType(CardType.Link)) + .Concat((Bot.GetMonsters() ?? Enumerable.Empty()) + .Where(c => c.HasType(CardType.Link) && c != Card)); + + List costList = costPool + .OrderBy(c => c.Location != CardLocation.Grave) + .ThenBy(c => c.LinkCount) + .ThenBy(c => c.Attack) + .ToList(); + + if (costList.Count == 0) + return false; + + List targets = new List(); + + targets.AddRange(Enemy.GetSpells() + .Where(s => s != null && s.IsFacedown())); + targets.AddRange(Enemy.GetSpells() + .Where(s => s != null && !s.IsFacedown())); + targets.AddRange(Enemy.GetMonsters() + .OrderByDescending(m => m.Attack)); + + if (targets.Count == 0) + return false; + + AI.SelectCard(costList); + AI.SelectNextCard(targets); + + return true; + } + private bool AlreadySSFromBanishThisTurn(ClientCard c) + { + if (c == null) return false; + if (c.Controller != 0) return false; + + if (c.IsCode(CardId.MalissP_Dormouse)) return ssDormouse; + if (c.IsCode(CardId.MalissP_WhiteRabbit)) return ssWhiteRabbit; + if (c.IsCode(CardId.MalissP_ChessyCat)) return ssChessyCat; + + if (c.IsCode(CardId.MalissQ_RedRansom)) return ssRRThisTurn; + if (c.IsCode(CardId.MalissQ_HeartsCrypter)) return ssHCThisTurn; + if (c.IsCode(CardId.MalissQ_WhiteBinder)) return ssWBThisTurn; + + return false; + } + private bool ShouldSkipBanishing(ClientCard c) + { + if (c == null) return true; + if (c.Controller != 0) return false; + if (AlreadySSFromBanishThisTurn(c)) return true; + if (c.IsCode(CardId.MalissP_MarchHare) && ActiveMarchHare) return true; + return false; + } + + private ClientCard PickMirrorCostCandidate() + { + int[] handPref = { + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat, + CardId.MalissP_Dormouse, + CardId.MalissP_MarchHare + }; + foreach (int id in handPref) + { + ClientCard h = Bot.Hand.GetFirstMatchingCard(c => c != null && c.IsCode(id)); + if (h != null) return h; + } + + ClientCard fieldP = Bot.GetMonsters() + .Where(c => c != null && c.HasSetcode(SetcodeMaliss) && !c.HasType(CardType.Link)) + .OrderBy(c => c.Attack).FirstOrDefault(); + if (fieldP != null) return fieldP; + + int[] avoid = { + CardId.MalissQ_HeartsCrypter, CardId.MalissQ_WhiteBinder, CardId.MalissQ_RedRansom + }; + ClientCard weakLink = Bot.GetMonsters() + .Where(c => c != null && c.HasSetcode(SetcodeMaliss) && c.HasType(CardType.Link) && !avoid.Contains(c.Id)) + .OrderBy(c => c.Attack).FirstOrDefault(); + return weakLink; + } + + private int[] Mirror_SearchOrderForType(bool isTrap, bool isMon) + { + if (isTrap) + return new[] { CardId.MalissC_GWC06, CardId.MalissC_MTP07 }; + if (isMon) + { + if (!Bot.HasInHand(CardId.MalissP_WhiteRabbit) && NSDorMouse) + { + return new[] { CardId.MalissP_WhiteRabbit, CardId.MalissP_MarchHare, CardId.MalissP_Dormouse, CardId.MalissP_ChessyCat }; + } + else + { + return new[] { CardId.MalissP_MarchHare, CardId.MalissP_Dormouse, CardId.MalissP_WhiteRabbit, CardId.MalissP_ChessyCat }; + } + } + return new[] { CardId.MalissP_MarchHare, CardId.MalissP_Dormouse, CardId.MalissP_WhiteRabbit, CardId.MalissP_ChessyCat }; + } + + private ClientCard PickMirrorGYTargetForSearch() + { + if (NSDorMouse && !ssRRThisTurn) + { + ClientCard rr = Bot.Graveyard.GetFirstMatchingCard( + c => c != null && c.IsCode(CardId.MalissQ_RedRansom)); + + if (rr != null) + return rr; + } + int[] monPref = { CardId.MalissP_MarchHare, CardId.MalissP_Dormouse, CardId.MalissP_WhiteRabbit, CardId.MalissP_ChessyCat }; + foreach (int id in monPref) + { + ClientCard m = Bot.Graveyard.GetFirstMatchingCard(c => c != null && c.IsCode(id)); + if (m != null && CheckRemainInDeck(id) > 0) return m; + } + return null; + } + private int PickMTP07SearchId() + { + if (CheckRemainInDeck(CardId.MalissP_MarchHare) > 0) + return CardId.MalissP_MarchHare; + + int[] pawnPref = { + CardId.MalissP_Dormouse, + CardId.MalissP_WhiteRabbit, + CardId.MalissP_ChessyCat + }; + foreach (int id in pawnPref) + if (CheckRemainInDeck(id) > 0) return id; + + return 0; + } + private void SelectSafeSTZoneAwayFromImperm() + { + List safeCols = Enumerable.Range(0, 5) + .Where(seq => + { + if (Bot.SpellZone[seq] != null) return false; + if (infiniteImpermanenceList.Contains(seq)) return false; + return true; + }) + .ToList(); + + if (safeCols.Count == 0) + { + safeCols = Enumerable.Range(0, 5) + .Where(seq => Bot.SpellZone[seq] == null) + .ToList(); + } + + int mask = 0; + foreach (int seq in safeCols) mask |= (1 << seq); + AI.SelectPlace(mask); + } + private bool T3Allow() + { + if (Duel.Player != 0) return false; + if (myTurnCount < 2) return false; + if (!HaveBackupOrWizardInHand() || !Bot.HasInHand(CardId.MalissP_MarchHare)) return false; + return true; + } + private bool NSBackup() + { + if (Bot.GetMonsterCount() != 0) return false; + if (usedNormalSummon) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) || Bot.HasInHand(CardId.MalissP_ChessyCat) || + Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.GoldSarcophagus)) return false; + nsBackupplan = true; + return true; + } + private bool NSBackup_L() + { + if (Bot.GetMonsterCount() != 0) return false; + if (usedNormalSummon) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) || Bot.HasInHand(CardId.MalissP_ChessyCat) || + Bot.HasInHand(CardId.MalissP_MarchHare) || Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.GoldSarcophagus)) return false; + return true; + } + + private bool NSMH() + { + int mhCount = Bot.Hand.GetMatchingCards(c => c != null && c.IsCode(CardId.MalissP_MarchHare)).Count; + if (mhCount <2 ) return false; + if (Bot.GetMonsterCount() != 0) return false; + if (usedNormalSummon) return false; + if (Bot.HasInHand(CardId.MalissP_Dormouse) || Bot.HasInHand(CardId.MalissP_WhiteRabbit) || Bot.HasInHand(CardId.MalissP_ChessyCat) || + Bot.HasInHand(CardId.MalissInUnderground) || Bot.HasInHand(CardId.TERRAFORMING) || + Bot.HasInHand(CardId.GoldSarcophagus)) return false; + return true; + } + private bool LinguribohMHLine() + { + if (!(Bot.HasInHand(CardId.MalissP_MarchHare) && Bot.HasInMonstersZone(CardId.MalissP_MarchHare))) return false; + if (Bot.GetMonsterCount() != 1) return false; + return true; + } + private bool EmerTranscode() + { + if (!enemyActivateLancea) return false; + if (Bot.GetMonsterCount() < 3) return false; + List mats = PickLinkMatsMinCount( + targetLink: 3, + isEligible: m => m.HasType(CardType.Effect), + minCount: 2, + maxCount: 2 + ); + if (mats.Count == 0) + { + return false; + } + + AI.SelectMaterials(mats); + return true; + } + #endregion + + // ======================= END OF FILE ==================== + } +} diff --git a/Game/AI/Decks/MalissOCGExecutor.cs b/Game/AI/Decks/MalissOCGExecutor.cs new file mode 100644 index 00000000..083b6816 --- /dev/null +++ b/Game/AI/Decks/MalissOCGExecutor.cs @@ -0,0 +1,1493 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using System; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; + +namespace WindBot.Game.AI.Decks +{ + [Deck("MalissOCG", "AI_MalissOCG")] + public class MalissOCGExecutor : DefaultExecutor + { + public class SetCode + { + public const int Maliss = 0x1bf; + } + public class CardId + { + public const int Artifact_Lancea = 34267821;//古遗物-圣枪 + public const int Dimension_Shifter = 91800273; //大宇宙人 + public const int MaxxG = 23434538; //增殖的G + public const int Mulcharmy_Fuwalos = 42141493; //欢聚友伴·茸茸长尾山雀 + public const int Infinite_Impermanence = 10045474; //无限泡影 + public const int Dominus_Impulse = 40366667; //灵王的波动 + public const int AshBlossom = 14558127; //灰流丽 + public const int CalledbytheGrave = 24224830; //墓穴的指名 + public const int Gold_Sarcophagus = 75500286; //封印之黄金柜 + public const int Wizard_Ignister = 3723262; //男巫@火灵天星 + public const int Backup_Ignister = 30118811; //备份员@火灵天星 + public const int Maliss_Chessy_Cat = 96676583; //码丽丝<兵卒>柴郡猫 + public const int Maliss_White_Rabbit = 69272449; //码丽丝<兵卒>白兔 + public const int Maliss_Dormouse = 32061192; //码丽丝<兵卒>睡鼠 + public const int Maliss_March_Hare = 20938824; //码丽丝<兵卒>三月兔 + public const int Maliss_in_the_Mirror = 93453053; //码丽丝镜中奇像 + public const int Maliss_in_Underground = 68337209; //码丽丝梦游地下界 + public const int Maliss_GWC_06 = 20726052; //码丽丝<代码>GWC-06 + public const int Maliss_TB_11 = 57111661; //码丽丝<代码>TB-11 + public const int Maliss_MTP_07 = 94722358; //码丽丝<代码>MTP-07 + + public const int Mereologic_Aggregator = 9940036; + public const int Cyberse_Desavewurm = 92422871; + public const int Allied_Code_Talker_Ignister = 39138610; //协心代码语者@火灵天星 + public const int Firewall_Dragon = 64211118; //防火龙·暗流体-新电磁泄密风 + public const int Accesscode_Talker = 86066372; //访问码语者 + public const int Maliss_Hearts_Crypter = 21848500; //码丽丝<王后>红心加密 + public const int Maliss_Red_Ransom = 68059897; //码丽丝<王后>红棋勒索 + public const int Maliss_White_Binder = 95454996; //码丽丝<王后>白棋捆绑 + public const int Transcode_Talker = 46947713; //转码语者 + public const int Splash_Mage = 59859086; //飞溅闪屏法师 + public const int Haggard_Lizardose = 9763474; //盛悴之致命毒蜥 + public const int Cyberse_Wicckid = 52698008; //电子界小男巫 + public const int Link_Decoder = 30342076; //连接解码员 + } + + public CardCount Count = new CardCount(); + public MalissOCGExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + AddExecutor(ExecutorType.Repos, MonsterRepos); + AddExecutor(ExecutorType.GoToBattlePhase, GoToBattlePhase); + AddExecutor(ExecutorType.Activate, CardId.Dimension_Shifter, Effect_Enemy_Turn); + AddExecutor(ExecutorType.Activate, CardId.Mulcharmy_Fuwalos, Effect_Enemy_Turn); + AddExecutor(ExecutorType.Activate, CardId.MaxxG, Effect_Enemy_Turn); + AddExecutor(ExecutorType.Activate, CardId.Infinite_Impermanence, Effect_Infinite_Impermanence); + AddExecutor(ExecutorType.Activate, CardId.AshBlossom, DefaultAshBlossomAndJoyousSpring); + AddExecutor(ExecutorType.Activate, CardId.Dominus_Impulse, Effect_Enemy_Chain); + AddExecutor(ExecutorType.Activate, CardId.CalledbytheGrave, DefaultCalledByTheGrave); + AddExecutor(ExecutorType.Activate, CardId.Maliss_White_Rabbit, Effect_White_Rabbit); + AddExecutor(ExecutorType.Activate, CardId.Haggard_Lizardose, Effect_Haggard_Lizardose); + AddExecutor(ExecutorType.Activate, CardId.Splash_Mage); + AddExecutor(ExecutorType.Activate, CardId.Cyberse_Wicckid); + AddExecutor(ExecutorType.Activate, CardId.Cyberse_Desavewurm); + AddExecutor(ExecutorType.Activate, CardId.Transcode_Talker); + AddExecutor(ExecutorType.Activate, CardId.Mereologic_Aggregator, Effect_Mereologic_Aggregator); + AddExecutor(ExecutorType.Activate, CardId.Firewall_Dragon, Effect_Firewall_Dragon); + AddExecutor(ExecutorType.Activate, CardId.Allied_Code_Talker_Ignister, Effect_Allied_Code_Talker_Ignister); + AddExecutor(ExecutorType.Activate, CardId.Maliss_White_Binder, Effect_Maliss_Link); + AddExecutor(ExecutorType.Activate, CardId.Maliss_Red_Ransom, Effect_Maliss_Link); + AddExecutor(ExecutorType.Activate, CardId.Maliss_Hearts_Crypter, Effect_Maliss_Hearts_Crypter); + AddExecutor(ExecutorType.Activate, CardId.Link_Decoder); + + AddExecutor(ExecutorType.Summon, CardId.Maliss_Dormouse, Summon_Maliss_Dormouse); + AddExecutor(ExecutorType.Summon, CardId.Maliss_White_Rabbit, Summon_Maliss_White_Rabbit); + AddExecutor(ExecutorType.Summon, CardId.Maliss_Chessy_Cat, Summon_Maliss_Chessy_Cat); + AddExecutor(ExecutorType.Summon, CardId.Backup_Ignister, Summon_Backup_Ignister); + + AddExecutor(ExecutorType.Activate, CardId.Maliss_Dormouse, Effect_Maliss_Dormouse); + + AddExecutor(ExecutorType.SpSummon, CardId.Maliss_Red_Ransom, SP_Maliss_Link); + AddExecutor(ExecutorType.SpellSet, SpellSet_Maliss); + AddExecutor(ExecutorType.Activate, CardId.Maliss_Chessy_Cat, Effect_Maliss_Chessy_Cat); + + AddExecutor(ExecutorType.SpSummon, CardId.Splash_Mage, SP_Splash_Mage); + AddExecutor(ExecutorType.SpSummon, CardId.Haggard_Lizardose, SP_Haggard_Lizardose); + AddExecutor(ExecutorType.SpSummon, CardId.Link_Decoder, SP_Link_Decoder); + + AddExecutor(ExecutorType.SpSummon, CardId.Cyberse_Wicckid, SP_Cyberse_Wicckid); + + AddExecutor(ExecutorType.Activate, CardId.Maliss_in_Underground, Effect_Remove); + AddExecutor(ExecutorType.Activate, CardId.Gold_Sarcophagus, Effect_Remove); + + AddExecutor(ExecutorType.Activate, CardId.Maliss_TB_11, Effect_Maliss_TB_11); + AddExecutor(ExecutorType.Activate, CardId.Maliss_MTP_07, Effect_Maliss_MTP_07); + + AddExecutor(ExecutorType.Activate, CardId.Maliss_March_Hare, Effect_Maliss_March_Hare); + AddExecutor(ExecutorType.Activate, CardId.Backup_Ignister); + AddExecutor(ExecutorType.Activate, CardId.Wizard_Ignister, Effect_Wizard_Ignister); + + AddExecutor(ExecutorType.Activate, CardId.Maliss_in_the_Mirror, Effect_Maliss_in_the_Mirror); + + AddExecutor(ExecutorType.SpSummon, CardId.Maliss_Hearts_Crypter, SP_Maliss_Hearts_Crypter); + AddExecutor(ExecutorType.SpSummon, CardId.Maliss_White_Binder, SP_Maliss_White_Binder); + AddExecutor(ExecutorType.Activate, CardId.Maliss_GWC_06, Effect_Maliss_GWC_06); + AddExecutor(ExecutorType.SpSummon, CardId.Firewall_Dragon, SP_Firewall_Dragon); + AddExecutor(ExecutorType.SpSummon, CardId.Allied_Code_Talker_Ignister, SP_Allied_Code_Talker_Ignister); + AddExecutor(ExecutorType.SpellSet, CardId.Maliss_in_the_Mirror); + AddExecutor(ExecutorType.SpSummon, CardId.Transcode_Talker, SP_Transcode_Talker); + AddExecutor(ExecutorType.SpellSet, SpellSet); + } + + public class CardCount + { + public int Dimension_Shifter = 0; + public int Summon = 0; + public int Phase = 0; + public List Activate = new List(); + public List ActivateRemoved = new List(); + public List Position = new List(); + public List Set = new List(); + public List Oppo = new List(); + public void Clear() + { + Activate.Clear(); + ActivateRemoved.Clear(); + Position.Clear(); + Set.Clear(); + Oppo.Clear(); + if (Dimension_Shifter > 0) + Dimension_Shifter --; + if (Summon > 0) + Summon --; + } + public void AddActivateOppo(int id) + { + Oppo.Add(id); + } + public bool CheckActivateOppo(int id) + { + return !Oppo.Contains(id); + } + public void AddSummon() + { + Summon = 1; + } + public void AddCard(int id) + { + if (id == CardId.Dimension_Shifter) + Dimension_Shifter = 2; + else + Activate.Add(id); + } + public void AddSet(int id) + { + Set.Add(id); + } + public bool CheckSet(int id) + { + return !Set.Contains(id); + } + public void AddCardRemoved(int id) + { + ActivateRemoved.Add(id); + } + public void AddPosition(int id) + { + Position.Add(id); + } + public void AddPhase() + { + Phase ++; + } + public bool CheckCard(int id) + { + if (id == CardId.Dimension_Shifter) + return Dimension_Shifter == 0; + else + return !Activate.Contains(id); + } + public bool CheckCardRemoved(int id) + { + return !ActivateRemoved.Contains(id); + } + public bool CheckPosition(int id) + { + return !Position.Contains(id); + } + public int CheckPhase() + { + return Phase; + } + public bool CheckSummon() + { + return Summon == 0; + } + } + + private struct ZoneData + { + public int Zone; + public ClientCard[] CheckZone; + } + + public override void OnNewTurn() + { + Count.AddPhase(); + Count.Clear(); + base.OnNewTurn(); + } + public override void OnChaining(int player, ClientCard card) + { + if (card.Id == CardId.Dimension_Shifter || card.Id == CardId.Artifact_Lancea) + Count.AddCard(card.Id); + else if (player == 0) + if (card.Location == CardLocation.Removed) + Count.AddCardRemoved(card.Id); + else + Count.AddCard(card.Id); + if (player == 1) + Count.AddActivateOppo(card.Id); + } + public override void OnChainEnd() + { + if (DefaultCheckWhetherCardIdIsNegated(CardId.Dimension_Shifter) && !Count.CheckCard(Card.Id)) + Count.Dimension_Shifter = 0; + Count.Oppo.Clear(); + } + public override bool OnSelectYesNo(long desc) + { + if (desc == Util.GetStringId(CardId.Maliss_White_Binder, 3)) + return true; + if (desc == Util.GetStringId(CardId.Maliss_MTP_07, 3)) + return Enemy.GetMonsters().Count(i => !i.IsShouldNotBeTarget()) + Enemy.GetSpells().Count(i => !i.IsShouldNotBeTarget() && (i.HasType(CardType.Field | CardType.Continuous | CardType.Equip) || i.IsFacedown())) > 0; + return base.OnSelectYesNo(desc); + } + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone) + { + if (new List{CardId.Maliss_Red_Ransom, CardId.Splash_Mage}.Contains(cardId)) + AI.SendCustomChat(0); + + if (new List{CardId.Cyberse_Wicckid, CardId.Allied_Code_Talker_Ignister}.Contains(cardId)) + { + if ((Zones.z6 & available) > 0 && (Bot.MonsterZone[3] == null || Bot.MonsterZone[4] == null)) return Zones.z6; + if ((Zones.z5 & available) > 0 && (Bot.MonsterZone[0] == null || Bot.MonsterZone[1] == null)) return Zones.z5; + } + if (Bot.HasInMonstersZone(CardId.Cyberse_Wicckid) && Count.CheckCard(CardId.Cyberse_Wicckid)) + { + int seq = 0; + for (int i = 0; i < 7; ++i) + if (Bot.MonsterZone[i] != null && Bot.MonsterZone[i].IsCode(CardId.Cyberse_Wicckid)) seq = i; + if (seq == 5) + { + if ((Zones.z1 & available) > 0) return Zones.z1; + if ((Zones.z2 & available) > 0) return Zones.z2; + } + else if (seq == 6) + { + if ((Zones.z3 & available) > 0) return Zones.z3; + if ((Zones.z4 & available) > 0) return Zones.z4; + } + } + if (cardId == CardId.Transcode_Talker) + { + if ((Zones.z6 & available) > 0 && Bot.MonsterZone[3] == null) return Zones.z6; + if ((Zones.z5 & available) > 0 && Bot.MonsterZone[1] == null) return Zones.z5; + if ((Zones.z0 & available) > 0 && Bot.MonsterZone[1] == null) return Zones.z0; + if ((Zones.z1 & available) > 0 && Bot.MonsterZone[2] == null) return Zones.z1; + if ((Zones.z2 & available) > 0 && Bot.MonsterZone[3] == null) return Zones.z2; + if ((Zones.z3 & available) > 0 && Bot.MonsterZone[4] == null) return Zones.z3; + } + if (cardId == CardId.Allied_Code_Talker_Ignister) + { + ZoneData[] zoneData = new ZoneData[] { + new ZoneData { Zone = Zones.z0, CheckZone = new ClientCard[] { Bot.MonsterZone[1] } }, + new ZoneData { Zone = Zones.z1, CheckZone = new ClientCard[] { Bot.MonsterZone[0], Bot.MonsterZone[2] } }, + new ZoneData { Zone = Zones.z2, CheckZone = new ClientCard[] { Bot.MonsterZone[1], Bot.MonsterZone[3] } }, + new ZoneData { Zone = Zones.z3, CheckZone = new ClientCard[] { Bot.MonsterZone[2], Bot.MonsterZone[4] } }, + new ZoneData { Zone = Zones.z4, CheckZone = new ClientCard[] { Bot.MonsterZone[3] } }, + new ZoneData { Zone = Zones.z5, CheckZone = new ClientCard[] { Bot.MonsterZone[0], Bot.MonsterZone[1], Bot.MonsterZone[2] } }, + new ZoneData { Zone = Zones.z6, CheckZone = new ClientCard[] { Bot.MonsterZone[2], Bot.MonsterZone[3], Bot.MonsterZone[4] } } + }; + + int maxNullCount = -1; + int selectedZone = 0; + + foreach (ZoneData data in zoneData) + { + if ((data.Zone & available) == 0) + continue; + + int nullCount = data.CheckZone.Count(card => card == null); + if (nullCount > maxNullCount) + { + maxNullCount = nullCount; + selectedZone = data.Zone; + } + } + + if (maxNullCount >= 0) + { + return selectedZone; + } + } + if ((Zones.z6 & available) > 0) return Zones.z6; + if ((Zones.z5 & available) > 0) return Zones.z5; + } + return base.OnSelectPlace(cardId, player, location, available); + } + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + if (AI.HaveSelectedCards()) return null; + ClientCard card = Duel.GetCurrentSolvingChainCard(); + if (card == null) + card = Card; + switch (card.Id) + { + case CardId.Maliss_White_Rabbit: + if (cards.Any(i => i.Id == CardId.Maliss_TB_11) && Count.CheckCard(CardId.Maliss_TB_11)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_TB_11).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_GWC_06) && Count.CheckCard(CardId.Maliss_GWC_06)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_GWC_06).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_MTP_07) && Count.CheckCard(CardId.Maliss_MTP_07)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_MTP_07).ToList(), cards, min, max); + break; + case CardId.Maliss_in_Underground: + if (Count.CheckSummon()) + { + if (cards.Any(i => i.Id == CardId.Maliss_Dormouse && i.Location == CardLocation.Deck) + && !Bot.HasInHand(CardId.Maliss_Dormouse) + && Check_Maliss_Dormouse() + && Count.CheckCardRemoved(CardId.Maliss_Dormouse)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Dormouse && i.Location == CardLocation.Deck).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit && i.Location == CardLocation.Deck) + && !Bot.HasInHand(CardId.Maliss_White_Rabbit) + && Check_Maliss_White_Rabbit() + && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit && i.Location == CardLocation.Deck).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_March_Hare && i.Location == CardLocation.Deck) && Check_Maliss_March_Hare(CardLocation.Removed) && Count.CheckCard(CardId.Maliss_March_Hare)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat && i.Location == CardLocation.Deck) + && !Bot.HasInHand(CardId.Maliss_Chessy_Cat) + && Count.CheckCardRemoved(CardId.Maliss_Chessy_Cat)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat && i.Location == CardLocation.Deck).ToList(), cards, min, max); + } + else + { + if (cards.Any(i => i.Id == CardId.Maliss_Dormouse && i.Location == CardLocation.Deck) + && Count.CheckCardRemoved(CardId.Maliss_Dormouse) && Check_Maliss_Dormouse()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Dormouse).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit && i.Location == CardLocation.Deck) + && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit) && Check_Maliss_White_Rabbit()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_March_Hare && i.Location == CardLocation.Deck) && Check_Maliss_March_Hare(CardLocation.Removed) && Count.CheckCard(CardId.Maliss_March_Hare)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat && i.Location == CardLocation.Deck) + && Count.CheckCardRemoved(CardId.Maliss_Chessy_Cat)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + + } + return Util.CheckSelectCount(cards.Where(i => i.Location == CardLocation.Deck).ToList(), cards, min, max); + case CardId.Gold_Sarcophagus: + if (Count.CheckSummon()) + { + if (cards.Any(i => i.Id == CardId.Maliss_Dormouse) + && !Bot.HasInHand(CardId.Maliss_Dormouse) + && Check_Maliss_Dormouse() + && Count.CheckCardRemoved(CardId.Maliss_Dormouse)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Dormouse).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit) + && !Bot.HasInHand(CardId.Maliss_White_Rabbit) + && Check_Maliss_White_Rabbit() + && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat) + && !Bot.HasInHand(CardId.Maliss_Chessy_Cat) + && Check_Maliss_Chessy_Cat() + && Count.CheckCardRemoved(CardId.Maliss_Chessy_Cat)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_March_Hare) + && Check_Maliss_March_Hare(CardLocation.Removed)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + } + else + { + if (cards.Any(i => i.Id == CardId.Maliss_Dormouse) + && Count.CheckCardRemoved(CardId.Maliss_Dormouse) && Check_Maliss_Dormouse()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Dormouse).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit) + && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit) && Check_Maliss_White_Rabbit()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat) + && Count.CheckCardRemoved(CardId.Maliss_Chessy_Cat) && Check_Maliss_Chessy_Cat()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_March_Hare) && Check_Maliss_March_Hare(CardLocation.Removed)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + } + break; + case CardId.Maliss_Dormouse: + if (Count.CheckSummon()) + { + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit) + && !Bot.HasInHand(CardId.Maliss_White_Rabbit) + && Check_Maliss_White_Rabbit() + && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat) + && !Bot.HasInHand(CardId.Maliss_Chessy_Cat) + && Check_Maliss_Chessy_Cat() + && Count.CheckCardRemoved(CardId.Maliss_Chessy_Cat)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_March_Hare) && Check_Maliss_March_Hare(CardLocation.Removed)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + } + else + { + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit) + && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit) && Check_Maliss_White_Rabbit()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat) + && Count.CheckCardRemoved(CardId.Maliss_Chessy_Cat) && Check_Maliss_Chessy_Cat()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_March_Hare) && Check_Maliss_March_Hare(CardLocation.Removed)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + } + break; + case CardId.Maliss_TB_11: + if (hint == HintMsg.SpSummon) + { + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + { + if (cards.Any(i => i.Id == CardId.Maliss_Dormouse) && Count.CheckCardRemoved(CardId.Maliss_Dormouse)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Dormouse).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit) && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_March_Hare) && Count.CheckCardRemoved(CardId.Maliss_March_Hare)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss)).ToList(), cards, min, max); + } + if (cards.Any(i => i.Id == CardId.Maliss_Dormouse) && Count.CheckCard(CardId.Maliss_Dormouse)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Dormouse).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit) && Count.CheckCard(CardId.Maliss_White_Rabbit)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat) && Count.CheckCard(CardId.Maliss_Chessy_Cat)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + } + else if (hint == HintMsg.Remove) + { + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Link))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id) && i.HasType(CardType.Link)).ToList(), cards, min, max); + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + } + break; + case CardId.Maliss_MTP_07: + if (hint == HintMsg.AddToHand) + { + if (Duel.Player == 1 && cards.Any(i => i.Id == CardId.Maliss_March_Hare) && Count.CheckCard(CardId.Maliss_March_Hare) && Check_Maliss_March_Hare(CardLocation.Hand)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Dormouse) && Count.CheckCard(CardId.Maliss_Dormouse)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Dormouse).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit) && Count.CheckCard(CardId.Maliss_White_Rabbit)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat) && Count.CheckCard(CardId.Maliss_Chessy_Cat)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + } + else if (hint == HintMsg.Remove) + { + if (cards.Any(i => i.Controller == 1)) + return Util.CheckSelectCount(cards.Where(i => i.Controller == 1).ToList(), cards, min, max); + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Link))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id) && i.HasType(CardType.Link)).ToList(), cards, min, max); + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + } + break; + case CardId.Splash_Mage: + case CardId.Haggard_Lizardose: + case CardId.Cyberse_Wicckid: + if (hint == HintMsg.Remove) + { + if (cards.Any(i => Count.CheckCard(i.Id) && Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss) && i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCard(i.Id) && Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss) && i.Location == CardLocation.Grave).ToList(), cards, min, max); + if (cards.Any(i => i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => i.Location == CardLocation.Grave).ToList(), cards, min, max); + } + else if (hint == HintMsg.SpSummon) + { + if (cards.Any(i => i.HasSetcode(SetCode.Maliss))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss)).ToList(), cards, min, max); + } + else if (hint == HintMsg.Target) + { + if (cards.Any(i => i.IsCode(CardId.Haggard_Lizardose))) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Haggard_Lizardose)).ToList(), cards, min, max); + } + break; + case CardId.Maliss_Red_Ransom: + if (hint == HintMsg.AddToHand) + { + List chk_cards = Bot.Graveyard.ToList(); + chk_cards.AddRange(Bot.GetSpells()); + chk_cards.AddRange(Bot.Hand); + if (cards.Any(i => i.IsCode(CardId.Maliss_in_the_Mirror)) + && Check_Maliss_in_the_Mirror(CardLocation.Removed) + && chk_cards.Any(i => i.HasType(CardType.Trap)) + && (((Bot.HasInHand(CardId.Maliss_Chessy_Cat) && Count.CheckSummon()) || Bot.HasInMonstersZone(CardId.Maliss_Chessy_Cat)) && Count.CheckCard(CardId.Maliss_Chessy_Cat) + || Bot.HasInHand(CardId.Maliss_March_Hare) && Count.CheckCard(CardId.Maliss_March_Hare) + ) && Count.CheckCard(CardId.Maliss_in_the_Mirror) + ) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_in_the_Mirror)).ToList(), cards, min, max); + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_in_the_Mirror)).ToList(), cards, min, max); + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_in_Underground)).ToList(), cards, min, max); + } + else if (hint == HintMsg.Remove) + { + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + { + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat) && Count.CheckCardRemoved(CardId.Maliss_Chessy_Cat)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + if (cards.Any(i => !i.IsCode(CardId.Maliss_Chessy_Cat) && Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss))) + return Util.CheckSelectCount(cards.Where(i => !i.IsCode(CardId.Maliss_Chessy_Cat) && Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss)).ToList(), cards, min, max); + } + if (cards.Any(i => i.Id == CardId.Maliss_March_Hare) && Check_Maliss_March_Hare(CardLocation.Removed)) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_March_Hare).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Dormouse) + && Count.CheckCardRemoved(CardId.Maliss_Dormouse) && Check_Maliss_Dormouse()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Dormouse).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_White_Rabbit) + && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit) && Check_Maliss_White_Rabbit()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_White_Rabbit).ToList(), cards, min, max); + if (cards.Any(i => i.Id == CardId.Maliss_Chessy_Cat) + && Count.CheckCardRemoved(CardId.Maliss_Chessy_Cat) && Check_Maliss_Chessy_Cat()) + return Util.CheckSelectCount(cards.Where(i => i.Id == CardId.Maliss_Chessy_Cat).ToList(), cards, min, max); + } + break; + case CardId.Maliss_Chessy_Cat: + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + { + if (cards.Any(i => i.IsCode(CardId.Maliss_March_Hare)) && !Bot.Graveyard.Any(i => i.HasSetcode(SetCode.Maliss)) && !Bot.Hand.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id) && !i.IsCode(CardId.Maliss_March_Hare))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && !i.IsCode(CardId.Maliss_March_Hare))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + } + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && i.IsCode(CardId.Maliss_in_the_Mirror)) && Check_Maliss_in_the_Mirror(CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id) && i.IsCode(CardId.Maliss_in_the_Mirror)).ToList(), cards, min, max); + if (cards.Any(i => Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + break; + case CardId.Maliss_White_Binder: + if (hint == HintMsg.Remove) + { + List result = new List(); + int ct = 5 - Bot.GetMonstersInMainZone().Count; + if (ct > 0 && Count.CheckCard(CardId.Allied_Code_Talker_Ignister)) + { + result.AddRange(cards.Where(i => i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id) && i.Controller == 0 && i.HasType(CardType.Link))); + if (Duel.Player == 1) + result.AddRange(cards.Where(i => i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id) && i.Controller == 0 && i.IsCode(CardId.Maliss_White_Rabbit))); + result.AddRange(cards.Where(i => i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id) && i.Controller == 0 && i.HasType(CardType.Monster))); + result.AddRange(cards.Where(i => i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id) && i.Controller == 0 && i.HasType(CardType.Spell))); + } + result.AddRange(cards.Where(i => i.Controller == 1)); + result.AddRange(cards.Where(i => TrashCards(i.Id, CardLocation.Grave))); + result.AddRange(cards.Where(i => !i.HasSetcode(SetCode.Maliss) && !i.HasType(CardType.Trap))); + result.AddRange(cards.Where(i => !i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Trap))); + if (result.Count() > max) + result = result.Take(max).ToList(); + if (result.Count() > 0) + return Util.CheckSelectCount(result, cards, result.Count(), result.Count()); + if (cards.Any(i => TrashCards(i.Id, CardLocation.Grave))) + return Util.CheckSelectCount(cards.Where(i => TrashCards(i.Id, CardLocation.Grave)).ToList(), cards, min, min); + + return Util.CheckSelectCount(cards, cards, min, min); + } + else if (hint == HintMsg.Set) + { + if (cards.Any(i => i.IsCode(CardId.Maliss_GWC_06))) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_GWC_06)).ToList(), cards, min, max); + } + break; + case CardId.Maliss_March_Hare: + if (hint == HintMsg.AddToHand) + { + if (cards.Any(i => Count.CheckCard(i.Id) && i.IsCode(CardId.Maliss_March_Hare))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCard(i.Id) && i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (cards.Any(i => Count.CheckCard(i.Id) && !i.HasType(CardType.Link))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCard(i.Id) && !i.HasType(CardType.Link)).ToList(), cards, min, max); + if (cards.Any(i => !i.HasType(CardType.Link))) + return Util.CheckSelectCount(cards.Where(i => !i.HasType(CardType.Link)).ToList(), cards, min, max); + } + else if (hint == HintMsg.Remove) + { + if (Duel.Player == 1) + { + if (Bot.GetMonstersInMainZone().Count() > 3) + { + if (cards.Any(i => i.HasType(CardType.Spell) && i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => i.HasType(CardType.Spell) && i.Location == CardLocation.Grave).ToList(), cards, min, max); + if (cards.Any(i => !i.HasType(CardType.Link) && i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => !i.HasType(CardType.Link) && i.Location == CardLocation.Grave).ToList(), cards, min, max); + } + else + { + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && i.HasType(CardType.Link) && i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id) && i.HasType(CardType.Link) && i.Location == CardLocation.Grave).ToList(), cards, min, max); + } + } + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && !i.HasType(CardType.Trap) && i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id) && !i.HasType(CardType.Trap) && i.Location == CardLocation.Grave).ToList(), cards, min, max); + if (cards.Any(i => !i.HasType(CardType.Trap) && i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => !i.HasType(CardType.Trap) && i.Location == CardLocation.Grave).ToList(), cards, min, max); + if (cards.Any(i => Count.CheckCardRemoved(i.Id) && !i.HasType(CardType.Trap))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id) && !i.HasType(CardType.Trap)).ToList(), cards, min, max); + } + break; + case CardId.Maliss_in_the_Mirror: + if (hint == HintMsg.Remove) + { + if (!cards.Any(i => i.Location != CardLocation.Grave)) + { + if (cards.Any(i => i.HasType(CardType.Trap)) && !Bot.HasInHandOrInSpellZoneOrInGraveyard(CardId.Maliss_GWC_06) && !Bot.HasInBanished(CardId.Maliss_GWC_06)) + return Util.CheckSelectCount(cards.Where(i => i.HasType(CardType.Trap)).ToList(), cards, min, max); + } + else + { + if (cards.Any(i => i.IsCode(CardId.Maliss_Red_Ransom)) && Count.CheckCardRemoved(CardId.Maliss_Red_Ransom)) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_Red_Ransom)).ToList(), cards, min, max); + if (cards.Any(i => i.HasType(CardType.Link) && i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.HasType(CardType.Link) && i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + } + } + else if (hint == HintMsg.AddToHand) + { + if (cards.Any(i => i.IsCode(CardId.Maliss_GWC_06))) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_GWC_06)).ToList(), cards, min, max); + } + else if (hint == HintMsg.Disable) + { + if (cards.Contains(Util.GetLastChainCard())) + return Util.CheckSelectCount(new List() { Util.GetLastChainCard() }, cards, min, max); + } + break; + case CardId.Maliss_GWC_06: + if (hint == HintMsg.Remove) + { + if (cards.Any(i => Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + } + else if (hint == HintMsg.SpSummon) + { + if (cards.Any(i => i.IsCode(CardId.Maliss_White_Binder) && i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_White_Binder) && i.Location == CardLocation.Grave).ToList(), cards, min, max); + if (cards.Any(i => i.HasType(CardType.Link) && !Count.CheckCardRemoved(i.Id) && i.Location == CardLocation.Grave)) + return Util.CheckSelectCount(cards.Where(i => i.HasType(CardType.Link) && !Count.CheckCardRemoved(i.Id) && i.Location == CardLocation.Grave).ToList(), cards, min, max); + } + break; + case CardId.Mereologic_Aggregator: + if (cards.Any(i => i.Controller == 1 && Count.CheckActivateOppo(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.Controller == 1 && Count.CheckActivateOppo(i.Id)).ToList(), cards, min, max); + return Util.CheckSelectCount(cards.Where(i => i.Controller == 1).ToList(), cards, min, max); + case CardId.Firewall_Dragon: + if (Enemy.GetMonsters().Count(i => !i.IsShouldNotBeTarget() && i.IsFaceup()) + Enemy.GetSpells().Count(i => !i.IsShouldNotBeTarget() && i.HasType(CardType.Field | CardType.Continuous | CardType.Equip)) > 0 + && Duel.Player == 1 && cards.Any(i => i.IsCode(CardId.Mereologic_Aggregator)) + ) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Mereologic_Aggregator)).ToList(), cards, min, max); + if (cards.Any(i => i.IsCode(CardId.Cyberse_Desavewurm))) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Cyberse_Desavewurm)).ToList(), cards, min, max); + return Util.CheckSelectCount(cards.Where(i => i.Location == CardLocation.Deck).ToList(), cards, min, max); + case CardId.Allied_Code_Talker_Ignister: + if (hint == HintMsg.SpSummon) + { + if (cards.Any(i => !i.IsCode(CardId.Maliss_White_Binder))) + return Util.CheckSelectCount(cards.Where(i => !i.IsCode(CardId.Maliss_White_Binder)).ToList(), cards, max, max); + return base.OnSelectCard(cards, max, max, hint, false); + } + else if (hint == HintMsg.Release) + { + if (cards.Any(i => i.LinkCount < 4)) + return Util.CheckSelectCount(cards.Where(i => i.LinkCount < 4).ToList(), cards, max, max); + } + break; + case CardId.Backup_Ignister: + if (hint == HintMsg.AddToHand) + { + if (card.Id == CardId.Dimension_Shifter || card.Id == CardId.Artifact_Lancea) + { + if (!Bot.Hand.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)) + && cards.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)) + ) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (!Bot.Hand.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)) + && cards.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)) + ) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + } + if (cards.Any(i => i.IsCode(CardId.Wizard_Ignister)) && Bot.Hand.Count() > 0 + && (Bot.Graveyard.Any(i => i.HasRace(CardRace.Cyberse)) + || (Bot.HasInExtra(CardId.Link_Decoder) && Bot.GetMonsters().Any(i => i.Level <= 4 && i.HasRace(CardRace.Cyberse)) && Count.CheckCard(CardId.Dimension_Shifter)) + || (Bot.HasInExtra(CardId.Haggard_Lizardose) + && !Count.CheckCard(CardId.Dimension_Shifter) + && Count.CheckCard(CardId.Artifact_Lancea) + && Bot.GetMonsters() + .Where(i => i.IsFaceup() && (!i.HasType(CardType.Link) || i.LinkCount < 2)).ToList() + .GroupBy(i => i.Id) + .Select(i => i.First()) + .Count() >= 2) + ) + ) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Wizard_Ignister)).ToList(), cards, min, max); + if (Bot.HasInHand(CardId.Maliss_March_Hare)) + { + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare) && Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare) && Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + } + else + if (cards.Any(i => i.IsCode(CardId.Maliss_March_Hare))) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss)).ToList(), cards, min, max); + } + else if (hint == HintMsg.Discard) + { + if (card.Id == CardId.Dimension_Shifter || card.Id == CardId.Artifact_Lancea) + { + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id) && !i.IsCode(CardId.Maliss_March_Hare))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && !i.IsCode(CardId.Maliss_March_Hare))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && !i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + } + if (Bot.HasInHand(CardId.Maliss_March_Hare) && !Bot.Graveyard.Any(i => i.HasSetcode(SetCode.Maliss))) + { + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare) && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare) && Count.CheckCardRemoved(i.Id))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare) && Count.CheckCardRemoved(i.Id)).ToList(), cards, min, max); + if (cards.Any(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss) && !i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + if (cards.Count(i => i.IsCode(CardId.Maliss_March_Hare)) > 1) + return Util.CheckSelectCount(cards.Where(i => i.IsCode(CardId.Maliss_March_Hare)).ToList(), cards, min, max); + } + if (cards.Any(i => TrashCards(i.Id, CardLocation.Hand))) + return Util.CheckSelectCount(cards.Where(i => TrashCards(i.Id, CardLocation.Hand)).ToList(), cards, min, max); + if (cards.Any(i => !i.HasType(CardType.Monster))) + return Util.CheckSelectCount(cards.Where(i => !i.HasType(CardType.Monster)).ToList(), cards, min, max); + if (cards.Any(i => !i.HasRace(CardRace.Cyberse))) + return Util.CheckSelectCount(cards.Where(i => !i.HasRace(CardRace.Cyberse)).ToList(), cards, min, max); + } + break; + case CardId.Wizard_Ignister: + if (cards.Any(i => i.HasSetcode(SetCode.Maliss))) + return Util.CheckSelectCount(cards.Where(i => i.HasSetcode(SetCode.Maliss)).ToList(), cards, min, max); + break; + case CardId.Maliss_Hearts_Crypter: + if (hint == HintMsg.Remove && cards.Any(i => i.Controller == 1)) + return Util.CheckSelectCount(cards.Where(i => i.Controller == 1).ToList(), cards, min, max); + break; + } + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + private bool TrashCards(int code, CardLocation loc) + { + if (loc == CardLocation.Grave) + { + List list = new List{ + CardId.MaxxG, + CardId.Artifact_Lancea, + CardId.Dimension_Shifter, + CardId.Mulcharmy_Fuwalos, + CardId.Infinite_Impermanence, + CardId.Dominus_Impulse, + CardId.AshBlossom, + CardId.CalledbytheGrave, + CardId.Gold_Sarcophagus, + + }; + return list.Contains(code); + } + else if (loc == CardLocation.Hand) + { + if (Bot.GetFieldCount() > 0 && code == CardId.Mulcharmy_Fuwalos) + return true; + if (Bot.Graveyard.Count > 0 && code == CardId.Dimension_Shifter) + return true; + } + return false; + } + private bool MonsterRepos() + { + if (!Enemy.GetMonsters().Any(i => i.IsDefense()) + && Util.GetTotalAttackingMonsterAttack(0) + Card.Attack >= Enemy.LifePoints + Util.GetTotalAttackingMonsterAttack(1) + && Card.IsDefense() + ) + return true; + return Card.IsFacedown(); + } + private bool SpellSet() + { + return Card.HasType(CardType.Trap | CardType.QuickPlay); + } + private bool SpellSet_Maliss() + { + return Card.HasType(CardType.Trap) && Card.HasSetcode(SetCode.Maliss) && Bot.GetMonsters().Any(i => i.HasSetcode(SetCode.Maliss)); + } + private bool Effect_Enemy_Turn() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Card.Id == CardId.Dimension_Shifter) + { + if (Duel.Player == 1 && Count.CheckCard(Card.Id)) + { + Count.AddCard(Card.Id); + return true; + } + } + return Duel.Player == 1; + } + private bool Effect_Enemy_Chain() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + ClientCard LastChainCard = Util.GetLastChainCard(); + return LastChainCard != null && LastChainCard.Controller == 1; + } + private bool Effect_Infinite_Impermanence() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + + ClientCard LastChainCard = Util.GetLastChainCard(); + + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (Count.CheckPosition(this_seq)) return false; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ((this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || Util.IsChainTarget(Card) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + Count.AddPosition(this_seq); + return true; + } + } + else + { + if (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone) + { + AI.SelectCard(LastChainCard); + return true; + } + } + return false; + } + private bool Effect_Maliss_Removed(int lp = 300) + { + int ct = 5 - Bot.GetMonstersInMainZone().Count; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Card.HasType(CardType.Monster) && !Card.IsCode(CardId.Maliss_March_Hare) + && ct - Duel.CurrentChain.Count(i => i.HasSetcode(SetCode.Maliss) + && i.Location == CardLocation.Removed + && i.HasType(CardType.Monster) + ) <= 0 + ) return false; + if (Bot.LifePoints > lp && Card.Location == CardLocation.Removed) + { + Count.AddCardRemoved(Card.Id); + return true; + } + return false; + } + private bool Effect_Maliss_Chessy_Cat() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (ActivateDescription == Util.GetStringId(CardId.Maliss_Chessy_Cat, 0)) + { + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + { + if (Bot.Hand.Any(i => i.HasSetcode(SetCode.Maliss) + && Count.CheckCardRemoved(i.Id) + && !i.HasType(CardType.Trap) + && (!i.IsCode(CardId.Maliss_in_the_Mirror) || Check_Maliss_in_the_Mirror(CardLocation.Removed)) + )) + { + Count.AddCard(Card.Id); + return true; + } + if (Bot.HasInHand(CardId.Maliss_March_Hare) && !Bot.Graveyard.Any(i => i.HasSetcode(SetCode.Maliss))) + { + Count.AddCard(Card.Id); + return true; + } + return false; + } + + if (Bot.Hand.Any(i => i.HasSetcode(SetCode.Maliss) + && Count.CheckCardRemoved(i.Id) + && !i.HasType(CardType.Trap) + )) + { + Count.AddCard(Card.Id); + return true; + } + return false; + } + else + return Effect_Maliss_Removed(); + } + private bool Effect_Maliss_March_Hare() + { + if (Util.GetLastChainCard() != null && Util.GetLastChainCard().IsCode(CardId.Allied_Code_Talker_Ignister)) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Card.Location == CardLocation.Hand) + { + if (Duel.Player == 1 + && (!Bot.Graveyard.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Link)) + || Bot.GetMonstersInMainZone().Count() > 3) + ) + return false; + if (Bot.HasInMonstersZone(CardId.Maliss_Chessy_Cat) && Count.CheckCard(CardId.Maliss_Chessy_Cat)) + return false; + if (Check_Maliss_March_Hare(CardLocation.Hand)) + { + Count.AddCard(Card.Id); + return true; + } + return false; + } + else + return Effect_Maliss_Removed(); + } + private bool Effect_Maliss_Dormouse() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Card.Location == CardLocation.MonsterZone) + { + Count.AddCard(Card.Id); + return true; + } + else + return Effect_Maliss_Removed(); + } + private bool Summon_Maliss_Chessy_Cat() + { + if (Check_Maliss_Chessy_Cat()) + { + Count.AddSummon(); + return true; + } + return false; + } + private bool Effect_White_Rabbit() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Card.Location == CardLocation.MonsterZone) + { + Count.AddCard(Card.Id); + return true; + } + else + return Effect_Maliss_Removed(); + } + private bool Summon_Maliss_Dormouse() + { + if (Check_Maliss_Dormouse()) + { + Count.AddSummon(); + return true; + } + return false; + } + private bool Summon_Maliss_White_Rabbit() + { + if (Check_Maliss_White_Rabbit()) + { + Count.AddSummon(); + return true; + } + return false; + } + private bool Check_Maliss_in_the_Mirror(CardLocation loc) + { + if (loc == CardLocation.Removed) + { + if (!Count.CheckCard(CardId.Artifact_Lancea) || !Count.CheckCardRemoved(CardId.Maliss_March_Hare)) + return false; + List cards = Bot.Hand.ToList(); + cards.AddRange(Bot.GetMonsters()); + cards.AddRange(Bot.GetSpells()); + cards.AddRange(Bot.Graveyard); + cards.AddRange(Bot.Banished); + return Bot.Graveyard.Any(i => i.HasSetcode(SetCode.Maliss) + && ( + (i.HasType(CardType.Monster) && cards.Count(j => j.HasType(CardType.Monster)) < 10) + || (i.HasType(CardType.Spell) && cards.Count(j => j.HasType(CardType.Spell)) < 4) + || (i.HasType(CardType.Trap) && cards.Count(j => j.HasType(CardType.Trap)) < 3) + ) + ); + } + else + { + List cards = Bot.Hand.GetMonsters(); + cards.AddRange(Bot.GetMonsters()); + return cards.Any(i => Count.CheckCardRemoved(i.Id)); + } + } + private bool Check_Maliss_Chessy_Cat() + { + return Bot.Hand.Any(i => i.HasSetcode(SetCode.Maliss) + && !i.HasType(CardType.Trap) + && i != Card && Count.CheckCardRemoved(i.Id) + ) && Count.CheckCard(CardId.Maliss_Chessy_Cat); + } + private bool Check_Maliss_White_Rabbit() + { + return Bot.Graveyard.Count(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Trap)) < 3 + && Count.CheckCard(CardId.Maliss_White_Rabbit); + } + private bool Check_Maliss_Dormouse() + { + List cards = Bot.Hand.ToList(); + cards.AddRange(Bot.GetMonsters()); + cards.AddRange(Bot.GetSpells()); + cards.AddRange(Bot.Graveyard); + cards.AddRange(Bot.Banished); + return cards.Count(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster)) < 9 + && Count.CheckCard(CardId.Maliss_Dormouse); + } + private bool Check_Maliss_March_Hare(CardLocation loc) + { + if (loc == CardLocation.Removed) + { + if (!Count.CheckCard(CardId.Artifact_Lancea)) + return false; + return (Bot.Banished.Any(i => i.HasSetcode(SetCode.Maliss) + && i.HasType(CardType.Monster) + ) || Count.CheckCard(CardId.Maliss_March_Hare)) && Count.CheckCardRemoved(CardId.Maliss_March_Hare); + } + else + { + if (!Count.CheckCard(CardId.Artifact_Lancea)) + return false; + return Bot.Graveyard.GetMonsters().Any(i => i.HasSetcode(SetCode.Maliss)); + } + } + private bool Effect_Maliss_TB_11() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Bot.GetMonsterCount() > 1 + && (!Bot.HasInMonstersZone(CardId.Maliss_Red_Ransom) || !Bot.HasInMonstersZone(CardId.Maliss_White_Binder)) + ) + return false; + if (Bot.GetMonsters().Any(i => Count.CheckCardRemoved(i.Id))) + { + Count.AddCard(Card.Id); + return true; + } + if (Bot.HasInMonstersZone(CardId.Maliss_Red_Ransom) && Count.CheckCardRemoved(CardId.Maliss_Red_Ransom)) + { + Count.AddCard(Card.Id); + return true; + } + return false; + } + private bool Effect_Maliss_MTP_07() + { + if (Duel.LastChainPlayer == 0) return false; + if (DefaultCheckWhetherCardIsNegated(Card) || Duel.Player == 0 || Enemy.GetMonsters().Count(i => !i.IsShouldNotBeTarget()) + Enemy.GetSpells().Count(i => !i.IsShouldNotBeTarget() && (i.HasType(CardType.Field | CardType.Continuous | CardType.Equip) || i.IsFacedown())) == 0 || !Bot.GetMonsters().Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Link))) return false; + if (Bot.GetMonsterCount() > 1 + && (!Bot.HasInMonstersZone(CardId.Maliss_Red_Ransom) || !Bot.HasInMonstersZone(CardId.Maliss_White_Binder)) + ) + return false; + if (Bot.GetMonsters().Any(i => Count.CheckCardRemoved(i.Id)) + && ((Count.CheckCard(CardId.Maliss_March_Hare) && !Bot.HasInHand(CardId.Maliss_March_Hare) && Check_Maliss_March_Hare(CardLocation.Hand)) + || (Count.CheckSummon() && ( + (Count.CheckCard(CardId.Maliss_Dormouse) && !Bot.HasInMonstersZone(CardId.Maliss_Dormouse) && Check_Maliss_Dormouse()) + || (Count.CheckCard(CardId.Maliss_White_Rabbit) && !Bot.HasInMonstersZone(CardId.Maliss_White_Rabbit) && Check_Maliss_White_Rabbit()) + || (Count.CheckCard(CardId.Maliss_Chessy_Cat) && !Bot.HasInMonstersZone(CardId.Maliss_Chessy_Cat) && Check_Maliss_Chessy_Cat()) + ) + ) + ) + ) + { + Count.AddCard(Card.Id); + return true; + } + if (Bot.HasInMonstersZone(CardId.Maliss_Red_Ransom) && Count.CheckCardRemoved(CardId.Maliss_Red_Ransom)) + { + Count.AddCard(Card.Id); + return true; + } + return false; + } + private bool Effect_Maliss_GWC_06() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Duel.Player == 0 && Bot.HasInGraveyard(CardId.Maliss_White_Binder)) + return false; + if (Bot.GetMonsters().Any(i => Count.CheckCardRemoved(i.Id)) && Bot.Graveyard.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Link))) + { + Count.AddCard(Card.Id); + return true; + } + return false; + } + private bool Effect_Remove() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + return ( + (!Bot.HasInMonstersZone(CardId.Maliss_Dormouse) && Count.CheckCard(CardId.Maliss_Dormouse) && Count.CheckCardRemoved(CardId.Maliss_Dormouse) && Check_Maliss_Dormouse()) + || (!Bot.HasInMonstersZone(CardId.Maliss_White_Rabbit) && Count.CheckCard(CardId.Maliss_White_Rabbit) && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit) && Check_Maliss_White_Rabbit()) + || (!Bot.HasInMonstersZone(CardId.Maliss_Chessy_Cat) && Count.CheckCard(CardId.Maliss_Chessy_Cat) && Count.CheckCardRemoved(CardId.Maliss_White_Rabbit) && Check_Maliss_Chessy_Cat()) + || (Count.CheckCardRemoved(CardId.Maliss_March_Hare) && Check_Maliss_March_Hare(CardLocation.Removed) && Bot.Banished.Any(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Monster))) + ); + } + private bool SP_Splash_Mage() + { + if (Bot.GetMonsters().Count(i => !i.HasType(CardType.Link) || i.LinkCount < 2) < 2 + && !(Bot.HasInMonstersZone(CardId.Maliss_Red_Ransom) + && Count.CheckCardRemoved(CardId.Maliss_Red_Ransom) + && Bot.GetMonsters().Count() == 2 + && Count.CheckCard(CardId.Maliss_White_Binder)) + ) + return false; + + bool chk = false; + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + chk = Bot.Graveyard.Any(i => i.HasType(CardType.Monster) && i.HasRace(CardRace.Cyberse) && !i.HasType(CardType.Link)); + else + chk = true; + + if (chk) + { + List materials = Bot.GetMonsters().Where(card => card != null && card.IsFaceup() && !card.HasType(CardType.Link)).ToList(); + AI.SelectMaterials(materials); + } + return chk; + } + private bool SP_Cyberse_Wicckid() + { + if (!Count.CheckCard(CardId.Artifact_Lancea)) + return false; + if (!Count.CheckCard(CardId.Dimension_Shifter) && !Bot.Graveyard.Any(i => i.HasRace(CardRace.Cyberse))) + return false; + if (Bot.HasInHand(CardId.Backup_Ignister) || !Count.CheckCard(CardId.Backup_Ignister)) + return false; + if (Bot.GetMonsters().Any(i => i.IsFaceup() && i.Level <= 4 && i.HasRace(CardRace.Cyberse)) + && Bot.GetMonsterCount() == 3 && Count.CheckCard(CardId.Backup_Ignister) && Bot.Hand.Count > 0 + && Bot.HasInExtra(CardId.Maliss_Hearts_Crypter) && Bot.HasInExtra(CardId.Link_Decoder)) + { + List materials = Bot.GetMonsters().Where(card => card.IsFaceup() && card.Sequence > 4).ToList(); + materials.AddRange(Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Link)).ToList()); + AI.SelectMaterials(materials); + return true; + } + if (Bot.GetMonsters().Count(i => !i.HasType(CardType.Link) || i.LinkCount < 2) < 2 + || (Bot.MonsterZone[5] != null && Bot.MonsterZone[5].HasType(CardType.Link) && Bot.MonsterZone[5].LinkCount > 3) + || (Bot.MonsterZone[6] != null && Bot.MonsterZone[6].HasType(CardType.Link) && Bot.MonsterZone[6].LinkCount > 3) + ) + return false; + if ((Bot.HasInHand(CardId.Backup_Ignister) && Count.CheckCard(CardId.Backup_Ignister) && Bot.GetMonstersInMainZone().Count() < 5) + || (Bot.HasInHand(CardId.Wizard_Ignister) && Count.CheckCard(CardId.Wizard_Ignister) + && Bot.Graveyard.Any(i => i.HasRace(CardRace.Cyberse) && i.HasAttribute(CardAttribute.Dark)) + && Bot.Graveyard.Count(i => i.HasRace(CardRace.Cyberse)) > 1 + && Bot.GetMonstersInMainZone().Count() < 4) + || (Bot.HasInHand(CardId.Maliss_March_Hare) && Count.CheckCard(CardId.Maliss_March_Hare) + && Check_Maliss_March_Hare(CardLocation.Hand) + && Bot.Graveyard.Count(i => i.HasRace(CardRace.Cyberse)) > 1 + && Bot.GetMonstersInMainZone().Count() < 4) + ) + { + List materials = Bot.GetMonsters().Where(card => card.IsFaceup() && card.Sequence > 4).ToList(); + materials.AddRange(Bot.GetMonsters().Where(card => card.IsFaceup() && !card.HasType(CardType.Link)).ToList()); + AI.SelectMaterials(materials); + return true; + } + return false; + } + private bool Effect_Haggard_Lizardose() + { + return Bot.Graveyard.Any(i => i.HasType(CardType.Monster) && i.Attack <= 2000); + } + private bool SP_Haggard_Lizardose() + { + List cards = Bot.GetMonsters().Where(i => i.IsFaceup() && (!i.HasType(CardType.Link) || i.LinkCount < 2)).ToList() + .GroupBy(i => i.Id) + .Select(i => i.First()) + .ToList(); + if (cards.Count < 2) + return false; + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea) && cards.Any(i => i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id))) + { + List materials = cards.Where(i => Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss)).ToList(); + materials.AddRange(cards.Where(i => !Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss))); + materials.AddRange(cards.Where(i => ! i.HasSetcode(SetCode.Maliss))); + AI.SelectMaterials(materials); + return true; + } + if (Bot.HasInExtra(CardId.Splash_Mage)) + return false; + bool chk = false; + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + chk = Bot.Graveyard.Any(i => i.HasType(CardType.Monster) && i.BaseAttack <= 2000 && Count.CheckCardRemoved(i.Id) && Count.CheckCard(i.Id) && i.HasSetcode(SetCode.Maliss)); + else + chk = Bot.GetMonsters().Any(i => i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && i.BaseAttack <= 2000 && i.HasSetcode(SetCode.Maliss)) + || Bot.Graveyard.Any(i => i.HasType(CardType.Monster) && Count.CheckCardRemoved(i.Id) && i.BaseAttack <= 2000 && i.HasSetcode(SetCode.Maliss)); + if (chk) + { + List materials = cards.Where(i => i.BaseAttack <= 2000 && Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss)).ToList(); + materials.AddRange(cards.Where(i => !materials.Contains(i))); + AI.SelectMaterials(materials); + } + return chk; + } + private bool SP_Maliss_Link() + { + if (Bot.GetMonsters().Any(i => i.HasType(CardType.Link) && i.LinkCount == 2) && Bot.GetMonsters().Any(i => !i.HasType(CardType.Link) && i.HasSetcode(SetCode.Maliss)) + || (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea) + && !(Bot.HasInExtra(new int[] {CardId.Cyberse_Wicckid, CardId.Splash_Mage}) + && Bot.Graveyard.Any(i => i.HasRace(CardRace.Cyberse)) + ) && !(Bot.HasInExtra(CardId.Haggard_Lizardose) + && Bot.Graveyard.Any(i => i.HasSetcode(SetCode.Maliss) + && i.HasType(CardType.Monster) + && Count.CheckCardRemoved(i.Id) + ) + ) + ) + ) + { + List materials = Bot.GetMonsters().Where(card => card != null && card.IsFaceup() && card.LinkCount == 2).ToList(); + List cards = Bot.GetMonsters().Where(card => card != null && card.IsFaceup() && card.LinkCount < 2 && card.HasSetcode(SetCode.Maliss)).ToList(); + foreach (var card in cards) + { + if (materials.Count == 2) + break; + if (card.LinkCount > 2) + continue; + materials.Add(card); + } + AI.SelectMaterials(materials); + return true; + } + return false; + } + private bool Effect_Maliss_Link() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Card.Location == CardLocation.MonsterZone) + { + if (Card.IsCode(CardId.Maliss_White_Binder)) + { + if (ActivateDescription == Util.GetStringId(Card.Id, 1)) + { + Count.AddCard(Card.Id); + return true; + } + else if(!Bot.Graveyard.Any(i => i.HasSetcode(SetCode.Maliss) && Count.CheckCardRemoved(i.Id)) + && Enemy.Graveyard.Count() < 3) + { + Count.AddCard(Card.Id); + return true; + } + } + Count.AddCard(Card.Id); + return true; + } + else + return Effect_Maliss_Removed(900); + } + private bool SP_Link_Decoder() + { + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + { + if (Bot.GetMonsters().Any(i => i.HasSetcode(SetCode.Maliss) && i.Level <= 4 && Count.CheckCardRemoved(i.Id))){ + AI.SelectMaterials(Bot.GetMonsters().Where(i => i.HasSetcode(SetCode.Maliss) && i.Level <= 4 && Count.CheckCardRemoved(i.Id)).ToList()); + return true; + } + return false; + } + if (Bot.HasInHand(CardId.Maliss_March_Hare) && Count.CheckCard(CardId.Maliss_March_Hare) && Bot.GetMonsters().Any(i => i.HasSetcode(SetCode.Maliss) && i.Level <= 4 && Count.CheckCardRemoved(i.Id))) + { + AI.SelectMaterials(Bot.GetMonsters().Where(i => i.HasSetcode(SetCode.Maliss) && i.Level <= 4 && Count.CheckCardRemoved(i.Id)).ToList()); + return true; + } + if (Bot.GetMonsters().Any(i => i.LinkCount < 3 && i.HasSetcode(SetCode.Maliss)) && Bot.GetMonsters().Count(i => i.LinkCount < 3) >= 3) + { + AI.SelectMaterials(Bot.GetMonsters().Where(i => i.LinkCount < 3 && i.HasSetcode(SetCode.Maliss)).ToList()); + return true; + } + if (Bot.HasInMonstersZone(CardId.Cyberse_Wicckid)) + { + AI.SelectMaterials(CardId.Cyberse_Wicckid); + return true; + } + if (Bot.HasInMonstersZone(CardId.Backup_Ignister) && Bot.HasInHand(CardId.Wizard_Ignister)) + { + AI.SelectMaterials(CardId.Backup_Ignister); + return true; + } + + return false; + } + private bool Effect_Maliss_in_the_Mirror() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (ActivateDescription == Util.GetStringId(CardId.Maliss_in_the_Mirror, 0)) + { + ClientCard LastChainCard = Util.GetLastChainCard(); + return Duel.Player == 1 && LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone; + } + else + return Effect_Maliss_Removed(0); + } + private bool SP_Maliss_Hearts_Crypter() + { + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + { + if (Bot.HasInExtra(CardId.Maliss_Red_Ransom) || Bot.GetMonsters().Count(i => !i.HasType(CardType.Link) || i.LinkCount < 2) < (Bot.GetMonsters().Any(i => i.HasType(CardType.Link) && i.LinkCount == 2) ? 1 : 3)) + return false; + AI.SelectMaterials(Bot.GetMonsters().Where(i => !i.HasType(CardType.Link) || i.LinkCount < 2).ToList()); + return true; + } + if ((Bot.HasInMonstersZone(CardId.Link_Decoder) && Bot.GetMonsters().Count(i => !i.HasType(CardType.Link) || i.LinkCount <= 2) > 2) + || Bot.GetMonsters().Count(i => !i.HasType(CardType.Link) || i.LinkCount <= 2) > 4 + ) + { + AI.SelectMaterials(Bot.GetMonsters().Where(i => !i.HasType(CardType.Link) || i.LinkCount < 2).ToList()); + return true; + } + return false; + } + private bool SP_Maliss_White_Binder() + { + if (Bot.GetMonsters().Any(i => i.HasSetcode(SetCode.Maliss) && i.LinkCount == 3 && Count.CheckCardRemoved(i.Id))) + { + AI.SelectMaterials(Bot.GetMonsters().Where(i => Count.CheckCardRemoved(i.Id) && i.HasSetcode(SetCode.Maliss) || i.LinkCount < 3).ToList()); + return true; + } + return false; + } + private bool Effect_Maliss_Hearts_Crypter() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Card.Location == CardLocation.MonsterZone) + { + if (Enemy.GetMonsters().Count(i => !i.IsShouldNotBeTarget()) + Enemy.GetSpells().Count(i => !i.IsShouldNotBeTarget() && (i.HasType(CardType.Field | CardType.Continuous | CardType.Equip) || i.IsFacedown())) > 0 && Duel.LastChainPlayer != 0) + { + Count.AddCard(Card.Id); + return true; + } + return false; + } + else + return Effect_Maliss_Removed(900); + } + private bool SP_Firewall_Dragon() + { + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + return false; + List materials = Bot.GetMonsters().Where(i => i.IsCode(CardId.Maliss_White_Binder)).ToList(); + materials.AddRange(Bot.GetMonsters().Where(i => i.Sequence > 4 && i.HasType(CardType.Link) && i.LinkCount <= 3)); + materials.AddRange(Bot.GetMonsters().Where(i => i.HasSetcode(SetCode.Maliss) && !i.HasType(CardType.Link))); + materials.AddRange(Bot.GetMonsters().Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Link))); + materials.AddRange(Bot.GetMonsters().Where(i => i.Sequence < 5 && i.HasType(CardType.Link) && i.LinkCount <= 3)); + materials.AddRange(Bot.GetMonsters().Where(i => !i.HasType(CardType.Link))); + if (materials.Count > 3) + materials = materials.Take(3).ToList(); + AI.SelectMaterials(materials); + return true; + } + private bool SP_Allied_Code_Talker_Ignister() + { + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + return false; + if (Bot.GetMonsters().Count(i => i.LinkCount <= 3) < 3) return false; + List materials = Bot.GetMonsters().Where(i => i.IsCode(CardId.Maliss_White_Binder)).ToList(); + materials.AddRange(Bot.GetMonsters().Where(i => i.Sequence > 4 && i.HasType(CardType.Link) && i.LinkCount <= 3)); + materials.AddRange(Bot.GetMonsters().Where(i => i.HasSetcode(SetCode.Maliss) && !i.HasType(CardType.Link))); + materials.AddRange(Bot.GetMonsters().Where(i => i.HasSetcode(SetCode.Maliss) && i.HasType(CardType.Link))); + materials.AddRange(Bot.GetMonsters().Where(i => i.Sequence < 5 && i.HasType(CardType.Link) && i.LinkCount <= 3)); + materials.AddRange(Bot.GetMonsters().Where(i => !i.HasType(CardType.Link))); + if (materials.Count > 3) + materials = materials.Take(3).ToList(); + AI.SelectMaterials(materials); + return true; + } + private bool Effect_Allied_Code_Talker_Ignister() + { + if (ActivateDescription == Util.GetStringId(CardId.Allied_Code_Talker_Ignister, 1) && Duel.LastChainPlayer == 1) + { + if (Card.Sequence > 4) + { + return Bot.GetMonsters().Any(i => i.Sequence < 3 && (!i.HasType(CardType.Link) || (i.LinkCount <= 3 && Count.CheckCard(i.Id)))); + } + else + { + return Bot.GetMonsters().Any(i => i.Sequence < 5 && (i.Sequence - Card.Sequence == 1 || Card.Sequence - i.Sequence == 1) && (!i.HasType(CardType.Link) || (i.LinkCount <= 3 && Count.CheckCard(i.Id)))); + } + } + return true; + } + private bool Effect_Mereologic_Aggregator() + { + if (Enemy.GetMonsters().Count(i => !i.IsShouldNotBeTarget()) + Enemy.GetSpells().Count(i => !i.IsShouldNotBeTarget() && (i.HasType(CardType.Field | CardType.Continuous | CardType.Equip) || i.IsFacedown())) > 0) + { + ClientCard LastChainCard = Util.GetLastChainCard(); + if (LastChainCard != null && LastChainCard.Controller == 1 && (LastChainCard.Location == CardLocation.MonsterZone || LastChainCard.Location == CardLocation.SpellZone)) + AI.SelectCard(LastChainCard); + Count.AddCard(Card.Id); + return true; + } + return false; + } + private bool Effect_Firewall_Dragon() + { + if (!Count.CheckCard(CardId.Dimension_Shifter) && Count.CheckCard(CardId.Artifact_Lancea)) + return false; + return Duel.Player == 0 || ( + Enemy.GetMonsters().Count(i => !i.IsShouldNotBeTarget() && i.IsFaceup()) + Enemy.GetSpells().Count(i => !i.IsShouldNotBeTarget() && i.HasType(CardType.Field | CardType.Continuous | CardType.Equip)) > 0 + && Duel.LastChainPlayer != 0 + ); + } + private bool Summon_Backup_Ignister() + { + if (Bot.GetMonsters().Any(i => i.HasType(CardType.Link))) + return false; + Count.AddSummon(); + return true; + } + private bool SP_Transcode_Talker() + { + if (Bot.GetMonsters().Any(i => i.HasSetcode(SetCode.Maliss))) + return false; + if (!Bot.GetMonsters().Any(i => !i.HasType(CardType.Link) || i.LinkCount < 2)) + return false; + if (!Bot.GetMonsters().Any(i => i.HasType(CardType.Link) && i.LinkCount == 2)) + return false; + List materials = Bot.GetMonsters().Where(i => i.HasType(CardType.Link) && i.LinkCount == 2).ToList(); + materials.AddRange(Bot.GetMonsters().Where(i => i.IsCode(CardId.Link_Decoder))); + materials.AddRange(Bot.GetMonsters().Where(i => !i.HasType(CardType.Link) || i.LinkCount < 2 && i.Sequence == (materials[0].Sequence > 4 ? (materials[0].Sequence == 5 ? 1 : 3) : materials[0].Sequence + 1))); + materials.AddRange(Bot.GetMonsters().Where(i => !i.HasType(CardType.Link) || i.LinkCount < 2)); + if (materials.Count > 2) + materials = materials.Take(2).ToList(); + AI.SelectMaterials(materials); + return true; + } + private bool Effect_Wizard_Ignister() + { + return Card.Location == CardLocation.Hand; + } + private bool GoToBattlePhase() + { + if (!Enemy.GetMonsters().Any(i => i.IsDefense())) + { + if (Util.GetTotalAttackingMonsterAttack(0) >= Enemy.LifePoints + Util.GetTotalAttackingMonsterAttack(1)) + { + return true; + } + } + return false; + } + } +} diff --git a/Game/AI/Decks/MathMechExecutor.cs b/Game/AI/Decks/MathMechExecutor.cs index 00a97a32..55b82d24 100644 --- a/Game/AI/Decks/MathMechExecutor.cs +++ b/Game/AI/Decks/MathMechExecutor.cs @@ -1,4 +1,4 @@ -using System; +using System; using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using System.Diagnostics; @@ -376,4 +376,4 @@ public override int OnSelectPlace(long cardId, int player, CardLocation location } -} +} \ No newline at end of file diff --git a/Game/AI/Decks/OldSchoolExecutor.cs b/Game/AI/Decks/OldSchoolExecutor.cs index 04d9d616..bc62aa37 100644 --- a/Game/AI/Decks/OldSchoolExecutor.cs +++ b/Game/AI/Decks/OldSchoolExecutor.cs @@ -69,10 +69,10 @@ private bool DoubleSummon() if (_lastDoubleSummon == Duel.Turn) return false; - if (Main.SummonableCards.Count == 0) + if (Duel.MainPhase.SummonableCards.Count == 0) return false; - if (Main.SummonableCards.Count == 1 && Main.SummonableCards[0].Level < 5) + if (Duel.MainPhase.SummonableCards.Count == 1 && Duel.MainPhase.SummonableCards[0].Level < 5) { bool canTribute = false; foreach (ClientCard handCard in Bot.Hand) diff --git a/Game/AI/Decks/OrcustExecutor.cs b/Game/AI/Decks/OrcustExecutor.cs index 1dccb062..e63f6d7f 100644 --- a/Game/AI/Decks/OrcustExecutor.cs +++ b/Game/AI/Decks/OrcustExecutor.cs @@ -214,11 +214,13 @@ public override void OnNewTurn() CymbalSkeletonUsed = false; BorrelswordDragonUsed = false; RustyBardicheTarget = null; + base.OnNewTurn(); } public override void OnChainEnd() { RustyBardicheTarget = null; + base.OnChainEnd(); } public override CardPosition OnSelectPosition(int cardId, IList positions) @@ -465,6 +467,7 @@ private bool JetSynchronSummon() private bool JetSynchronEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; AI.SelectCard(HandCosts); return true; } @@ -488,6 +491,7 @@ private bool AlmirajSummon() private bool DestrudoSummon() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Bot.GetMonsterCount() < 3 && Bot.HasInExtra(new[] { CardId.CrystronNeedlefiber, CardId.KnightmarePhoenix }); } @@ -659,6 +663,7 @@ private bool KnightmarePhoenixSummon() private bool KnightmarePhoenixEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; int costcount = Bot.Hand.GetMatchingCardsCount(card => card.IsCode(HandCosts)); ClientCard target = Enemy.SpellZone.GetFloodgate(); ClientCard anytarget = Enemy.SpellZone.GetFirstMatchingCard(card => !card.OwnTargets.Any(cont => cont.IsCode(CardId.TrickstarLightStage))); @@ -685,6 +690,7 @@ private bool KnightmareMermaidSummon() private bool KnightmareMermaidEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; AI.SelectCard(HandCosts); return true; } @@ -703,6 +709,7 @@ private bool GalateaSummonFirst() private bool OrcustKnightmareEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (!Bot.HasInGraveyard(CardId.OrcustHarpHorror)) { AI.SelectCard(Util.GetBestBotMonster()); @@ -726,6 +733,7 @@ private bool OrcustKnightmareEffect() private bool HarpHorrorEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; HarpHorrorUsed = true; AI.SelectCard(CardId.OrcustCymbalSkeleton); return true; @@ -733,6 +741,7 @@ private bool HarpHorrorEffect() private bool WorldWandEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; AI.SelectCard(CardId.OrcustCymbalSkeleton); return true; } @@ -783,6 +792,7 @@ private ClientCard GetFogBladeTarget() private bool CymbalSkeletonEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; int[] botTurnTargets = new[] { CardId.GalateaTheOrcustAutomaton, CardId.SheorcustDingirsu }; int[] emenyTurnTargets = new[] { CardId.SheorcustDingirsu, CardId.GalateaTheOrcustAutomaton }; if (Duel.Player == 0 && Bot.HasInGraveyard(CardId.GalateaTheOrcustAutomaton) && !Bot.HasInMonstersZone(CardId.GalateaTheOrcustAutomaton) && Bot.HasInExtra(CardId.SheorcustDingirsu) && !SheorcustDingirsuSummoned) @@ -867,6 +877,7 @@ private bool SheorcustDingirsuEffect() private bool AncientCloakEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Bot.HasInMonstersZone(CardId.SalamangreatAlmiraj) && Bot.HasInExtra(CardId.KnightmarePhoenix)) AI.SelectCard(CardId.ThePhantomKnightsofShadeBrigandine); else @@ -881,6 +892,7 @@ private bool SilentBootsSummon() private bool SilentBootsEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Bot.HasInMonstersZone(CardId.SalamangreatAlmiraj) && Bot.HasInExtra(CardId.KnightmarePhoenix)) AI.SelectCard(CardId.ThePhantomKnightsofShadeBrigandine); else @@ -1033,6 +1045,7 @@ private bool BabelEffect() { if (Card.Location == CardLocation.Grave) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; IList costCards = Bot.Hand.GetMatchingCards(card => card.IsCode(HandCosts)); if (costCards.Count > 0) { @@ -1113,6 +1126,7 @@ private bool FogBladeEffect() } else if (Bot.HasInGraveyard(CardId.ThePhantomKnightsofRustyBardiche) || Bot.GetMonsterCount() < 2) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; AI.SelectCard(CardId.ThePhantomKnightsofRustyBardiche); return true; } diff --git a/Game/AI/Decks/PhantasmExecutor.cs b/Game/AI/Decks/PhantasmExecutor.cs index c83e76b9..8d0ada70 100644 --- a/Game/AI/Decks/PhantasmExecutor.cs +++ b/Game/AI/Decks/PhantasmExecutor.cs @@ -1,4 +1,4 @@ -using YGOSharp.OCGWrapper.Enums; +using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using WindBot; using WindBot.Game; diff --git a/Game/AI/Decks/PureWindsExecutor.cs b/Game/AI/Decks/PureWindsExecutor.cs index a7714dfc..63776671 100644 --- a/Game/AI/Decks/PureWindsExecutor.cs +++ b/Game/AI/Decks/PureWindsExecutor.cs @@ -555,6 +555,7 @@ private bool DaigustoGulldoseff() } private bool SpeedroidTaketomborgeff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if ((Bot.GetRemainingCount(CardId.SpeedroidRedEyedDice, 1) >= 1) && Bot.HasInMonstersZone(CardId.SpeedroidTerrortop)) { diff --git a/Game/AI/Decks/QliphortExecutor.cs b/Game/AI/Decks/QliphortExecutor.cs index 5b12750f..483d2dc1 100644 --- a/Game/AI/Decks/QliphortExecutor.cs +++ b/Game/AI/Decks/QliphortExecutor.cs @@ -130,6 +130,7 @@ public override bool OnSelectHand() public override void OnNewTurn() { CardOfDemiseUsed = false; + base.OnNewTurn(); } public override IList OnSelectPendulumSummon(IList cards, int max) @@ -281,6 +282,7 @@ private bool ScoutEffect() { if (Card.Location == CardLocation.Hand) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; int count = 0; int handcount = 0; int fieldcount = 0; diff --git a/Game/AI/Decks/RainbowExecutor.cs b/Game/AI/Decks/RainbowExecutor.cs index c98c71df..03de70b4 100644 --- a/Game/AI/Decks/RainbowExecutor.cs +++ b/Game/AI/Decks/RainbowExecutor.cs @@ -142,6 +142,7 @@ public RainbowExecutor(GameAI ai, Duel duel) public override void OnNewTurn() { NormalSummoned = false; + base.OnNewTurn(); } public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) @@ -210,6 +211,7 @@ private bool UnexpectedDaiEffect() private bool RescueRabbitSummon() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Util.GetBotAvailZonesFromExtraDeck() > 0 || !Enemy.MonsterZone.IsExistingMatchingCard(card => card.GetDefensePower() >= 1900) || Enemy.MonsterZone.GetMatchingCardsCount(card => card.GetDefensePower() < 1900) > Bot.MonsterZone.GetMatchingCardsCount(card => card.Attack >= 1900); diff --git a/Game/AI/Decks/Rank5Executor.cs b/Game/AI/Decks/Rank5Executor.cs index 2a3ab12b..f3c72170 100644 --- a/Game/AI/Decks/Rank5Executor.cs +++ b/Game/AI/Decks/Rank5Executor.cs @@ -116,6 +116,7 @@ public override void OnNewTurn() DoubleSummonUsed = false; CyberDragonInfinitySummoned = false; Number61VolcasaurusUsed = false; + base.OnNewTurn(); } public override IList OnSelectXyzMaterial(IList cards, int min, int max) diff --git a/Game/AI/Decks/RyzealExecutor.cs b/Game/AI/Decks/RyzealExecutor.cs new file mode 100644 index 00000000..eab7d542 --- /dev/null +++ b/Game/AI/Decks/RyzealExecutor.cs @@ -0,0 +1,4192 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using System; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Ryzeal", "AI_Ryzeal")] + public class RyzealExecutor : DefaultExecutor + { + public class CardId + { + public const int IceRyzeal = 8633261; + public const int SwordRyzeal = 35844557; + public const int NodeRyzeal = 72238166; + public const int ExRyzeal = 34022970; + // _CardId.MulcharmyFuwalos; + // _CardId.MulcharmyPurulia; + // _CardId.MulcharmyNyalus; + // _CardId.AshBlossom; + // _CardId.GhostOgreAndSnowRabbit; + // _CardId.MaxxC; + // _CardId.LockBird; + // _CardId.EffectVeiler; + + public const int SeventhTachyon = 7477101; + public const int TripleTacticsTalent = 25311006; + // _CardId.PotOfDesires; + public const int Bonfire = 85106525; + // _CardId.CalledByTheGrave; + public const int RyzealPlugIn = 60394026; + // _CardId.CrossoutDesignator; + public const int RyzealCross = 6798031; + + // _CardId.InfiniteImpermanence = 10045474; + + public const int MereologicAggregator = 9940036; + public const int RyzealDetonator = 34909328; + public const int Number104Masquerade = 2061963; + public const int RyzealDuodrive = 7511613; + public const int TwinsOfTheEclipse = 45852939; + public const int FullArmoredUtopicRayLancer = 1269512; + public const int TornadoDragon = 6983839; + // _CardId.Number41BagooskatheTerriblyTiredTapir + // _CardId.EvilswarmExcitonKnight + public const int StarliegePhotonBlastDragon = 16643334; + public const int AbyssDweller = 21044178; + public const int Number60DugaresTheTimeless = 66011101; + public const int DonnerDaggerFurHire = 8728498; + } + + public RyzealExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + // counter + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, _CardId.CrossoutDesignator, CrossoutDesignatorActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, _CardId.EffectVeiler, EffectVeilerActivate); + AddExecutor(ExecutorType.Activate, _CardId.GhostOgreAndSnowRabbit, GhostOgreAndSnowRabbitActivate); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + + AddExecutor(ExecutorType.Activate, CardId.RyzealCross, RyzealCrossActivateCard); + AddExecutor(ExecutorType.Activate, _CardId.EvilswarmExcitonKnight, EvilswarmExcitonKnightActivate); + AddExecutor(ExecutorType.Activate, CardId.RyzealDetonator, RyzealDetonatorActivate); + AddExecutor(ExecutorType.Activate, CardId.RyzealDuodrive, RyzealDuodriveActivate); + AddExecutor(ExecutorType.Activate, CardId.TwinsOfTheEclipse, TwinsOfTheEclipseActivate); + AddExecutor(ExecutorType.Activate, CardId.AbyssDweller, AbyssDwellerActivate); + AddExecutor(ExecutorType.Activate, CardId.TornadoDragon, TornadoDragonActivate); + + // hand effect + AddExecutor(ExecutorType.Activate, _CardId.LockBird, LockBirdActivate); + AddExecutor(ExecutorType.Activate, _CardId.MulcharmyPurulia, MulcharmyPuruliaActivate); + AddExecutor(ExecutorType.Activate, _CardId.MulcharmyNyalus, MulcharmyNyalusActivate); + AddExecutor(ExecutorType.Activate, _CardId.MulcharmyFuwalos, MulcharmyFuwalosActivate); + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + + // pre-action activate + AddExecutor(ExecutorType.Activate, CardId.Bonfire, BonfireActivate); + AddExecutor(ExecutorType.Activate, CardId.DonnerDaggerFurHire, DonnerDaggerFurHireActivate); + AddExecutor(ExecutorType.Activate, CardId.Number60DugaresTheTimeless, Number60DugaresTheTimelessActivate); + AddExecutor(ExecutorType.Activate, CardId.TripleTacticsTalent, TripleTacticsTalentActivate); + AddExecutor(ExecutorType.Activate, CardId.Bonfire, BonfireActivateToSearchNecessary); + AddExecutor(ExecutorType.Activate, CardId.SeventhTachyon, SeventhTachyonActivate); + + AddExecutor(ExecutorType.Repos, ChangePositionFirst); + + // xyz summon + AddExecutor(ExecutorType.SpSummon, _CardId.EvilswarmExcitonKnight, EvilswarmExcitonKnightSpSummon); + AddExecutor(ExecutorType.SpSummon, LessSpSummonExtra); + AddExecutor(ExecutorType.SpSummon, CardId.RyzealDuodrive, FirstRyzealDuodriveSpSummon); + AddExecutor(ExecutorType.SpSummon, SecondXyzSummon); + AddExecutor(ExecutorType.SpSummon, CardId.TwinsOfTheEclipse, TwinsOfTheEclipseSpSummon); + AddExecutor(ExecutorType.SpSummon, FinalXyzSummon); + AddExecutor(ExecutorType.SpSummon, CardId.DonnerDaggerFurHire, DonnerDaggerFurHireSpSummon); + + AddExecutor(ExecutorType.Activate, _CardId.PotOfDesires, PotOfDesireActivateForContinue); + + AddExecutor(ExecutorType.Activate, CardId.RyzealPlugIn, RyzealPlugInActivateFirst); + AddExecutor(ExecutorType.Activate, CardId.NodeRyzeal, NodeRyzealActivateFirst); + AddExecutor(ExecutorType.Activate, CardId.RyzealCross, RyzealCrossActivateRecycleFirst); + + // summon/spsummon + AddExecutor(ExecutorType.SpSummon, CardId.IceRyzeal, IceRyzealSpSummonFirst); + AddExecutor(ExecutorType.SpSummon, CardId.NodeRyzeal, NodeRyzealSpSummonFirst); + + AddExecutor(ExecutorType.Summon, CardId.ExRyzeal, ExRyzealSummon); + AddExecutor(ExecutorType.SpSummon, CardId.ExRyzeal, ExRyzealSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SwordRyzeal, SwordRyzealSpSummon); + AddExecutor(ExecutorType.Summon, CardId.IceRyzeal, IceRyzealSummon); + AddExecutor(ExecutorType.SpSummon, CardId.ExRyzeal, ExRyzealSpSummonLater); + AddExecutor(ExecutorType.Summon, CardId.SwordRyzeal, SwordRyzealSummon); + AddExecutor(ExecutorType.SpSummon, CardId.NodeRyzeal, NodeRyzealSpSummon); + AddExecutor(ExecutorType.Summon, Level4Summon); + + AddExecutor(ExecutorType.Activate, CardId.NodeRyzeal, NodeRyzealActivate); + AddExecutor(ExecutorType.Activate, CardId.RyzealPlugIn, RyzealPlugInActivate); + AddExecutor(ExecutorType.SpSummon, CardId.IceRyzeal, IceRyzealSpSummon); + + // activate + AddExecutor(ExecutorType.Activate, CardId.MereologicAggregator, MereologicAggregatorActivateFirst); + AddExecutor(ExecutorType.Activate, CardId.IceRyzeal, IceRyzealActivate); + AddExecutor(ExecutorType.Activate, CardId.SwordRyzeal, SwordRyzealActivate); + AddExecutor(ExecutorType.Activate, CardId.ExRyzeal, ExRyzealActivate); + AddExecutor(ExecutorType.Activate, CardId.MereologicAggregator, MereologicAggregatorActivateLater); + + // after summon + AddExecutor(ExecutorType.Activate, CardId.RyzealCross, RyzealCrossActivateRecycleLater); + AddExecutor(ExecutorType.Activate, _CardId.PotOfDesires, PotOfDesiresActivate); + + AddExecutor(ExecutorType.Repos, DefaultMonsterRepos); + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + + } + + const int attrbuteLightDark = (int)CardAttribute.Light | (int)CardAttribute.Dark; + const int SetcodeTimeLord = 0x4a; + const int SetcodeAtlantean = 0x77; + const int SetcodeInfernoid = 0xbb; + const int SetcodeMajespecter = 0xd0; + const int SetcodePhantomKnight = 0x10db; + const int SetcodeSkyStriker = 0x115; + const int SetcodeOrcust = 0x11b; + const int SetcodeSangen = 0x1a9; + const int SetcodeTenpaiDragon = 0x1aa; + const int SetcodeBranded = 0x160; + const int SetcodeFloowandereeze = 0x16d; + const int SetcodeLabrynth = 0x17f; + const int SetcodeTearlaments = 0x182; + const int SetcodeHorus = 0x3; + const int SetcodeRyzeal = 0x1b6; + const int hintTimingMainEnd = 0x4; + List NotToNegateIdList = new List + { + 58699500, 20343502, 25451383, 19403423 + }; + List AlbazFusionList = new List + { + 1906812, 38811586, 41373230, 44146295, 51409648, 76666602, 87746184, 03410461, 34848821 + }; + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.IceRyzeal, CardId.SwordRyzeal, CardId.ExRyzeal, _CardId.AshBlossom, _CardId.EffectVeiler, CardId.SeventhTachyon, + _CardId.InfiniteImpermanence}}, + {2, new List { _CardId.MulcharmyFuwalos, _CardId.GhostOgreAndSnowRabbit, _CardId.MaxxC, _CardId.PotOfDesires, _CardId.CalledByTheGrave }}, + {1, new List { CardId.NodeRyzeal, _CardId.MulcharmyPurulia, _CardId.MulcharmyNyalus, _CardId.LockBird, CardId.TripleTacticsTalent, + CardId.Bonfire, CardId.RyzealPlugIn, _CardId.CrossoutDesignator, CardId.RyzealCross}} + }; + List NotToDestroySpellTrap = new List { 50005218, 6767771 }; + List targetNegateIdList = new List { + _CardId.EffectVeiler, _CardId.InfiniteImpermanence, _CardId.GhostMournerMoonlitChill, _CardId.BreakthroughSkill, CardId.MereologicAggregator, 74003290, 67037924, + 9753964, 66192538, 23204029, 73445448, 35103106, 30286474, 45002991, 5795980, 38511382, 53742162, 30430448 + }; + List NeedIceToSolveIdList = new List { 80978111, 87170768 }; + List currentCanActivateEffect = new List(); + + int maxSummonCount = 1; + int summonCount = 1; + bool enemyActivateMaxxC = false; + bool enemyActivatePurulia = false; + bool enemyActivateFuwalos = false; + bool enemyActivateNyalus = false; + bool lockBirdSolved = false; + int dimensionShifterCount = 0; + bool botActivateMulcharmy = false; + bool botSolvingCross = false; + List CheckSetcodeList = new List { SetcodePhantomKnight, SetcodeOrcust, SetcodeAtlantean, SetcodeRyzeal, SetcodeTenpaiDragon, SetcodeSangen, + SetcodeInfernoid, SetcodeSkyStriker, SetcodeLabrynth, SetcodeTearlaments }; + List CheckBotSolvedList = new List { _CardId.MaxxC, _CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, _CardId.MulcharmyNyalus, + CardId.AbyssDweller, _CardId.EvilswarmExcitonKnight, CardId.RyzealPlugIn }; + + bool enemyActivateInfiniteImpermanenceFromHand = false; + ClientCard DetonatorDestroySelf = null; + + List infiniteImpermanenceList = new List(); + List currentNegateCardList = new List(); + List currentDestroyCardList = new List(); + List activatedCardIdList = new List(); + List spSummonedCardIdList = new List(); + List botSolvedCardIdList = new List(); + List enemyPlaceThisTurn = new List(); + List summonThisTurn = new List(); + + List hardToDestroyCardList = new List(); + List cannotDestroyCardList = new List(); + HashSet enemyDeckTypeRecord = new HashSet(); + + ///

+ /// Shuffle List and return a random-order card list + /// + public List ShuffleList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + T tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } + + public bool CheckCanBeTargeted(ClientCard card, bool canBeTarget, CardType selfType) + { + if (card == null) return true; + if (canBeTarget) + { + if (card.IsShouldNotBeTarget()) return false; + if (((int)selfType & (int)CardType.Monster) > 0 && card.IsShouldNotBeMonsterTarget()) return false; + if (((int)selfType & (int)CardType.Spell) > 0 && card.IsShouldNotBeSpellTrapTarget()) return false; + if (((int)selfType & (int)CardType.Trap) > 0 + && (card.IsShouldNotBeSpellTrapTarget() && !card.IsDisabled())) return false; + } + return true; + } + + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + + public int CheckRemainInDeck(params int[] ids) + { + int sum = 0; + foreach (int id in ids) + { + sum += CheckRemainInDeck(id); + } + return sum; + } + + public bool CheckWhetherHaveFinalMonster() + { + foreach (ClientCard monster in Bot.MonsterZone) + { + if (monster == null) continue; + if (monster.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir) && monster.IsDefense()) return true; + if (monster.IsCode(CardId.AbyssDweller) && monster.Overlays.Count() > 0) return true; + if (monster.IsCode(CardId.RyzealDetonator) && monster.Overlays.Count() > 0) return true; + } + + return false; + } + + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated(bool disablecheck = true, bool toFieldCheck = false, CardType type = 0, bool ignore41 = false) + { + bool isMonster = type == 0 && Card.IsMonster(); + isMonster |= ((int)type & (int)CardType.Monster) != 0; + bool isSpellOrTrap = type == 0 && (Card.IsSpell() || Card.IsTrap()); + isSpellOrTrap |= (((int)type & (int)CardType.Spell) != 0) || (((int)type & (int)CardType.Trap) != 0); + bool isCounter = ((int)type & (int)CardType.Counter) != 0; + if (isSpellOrTrap && toFieldCheck && CheckSpellWillBeNegate(isCounter)) + return true; + if (DefaultCheckWhetherCardIsNegated(Card)) return true; + if (isMonster && (toFieldCheck || Card.Location == CardLocation.MonsterZone)) + { + if ((toFieldCheck && (((int)type & (int)CardType.Link) != 0)) || Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card, ignore41)) || Bot.MonsterZone.Any(card => CheckNumber41(card, ignore41))) return true; + } + if (Enemy.HasInSpellZone(_CardId.SkillDrain, true, true)) return true; + } + if (disablecheck) return (Card.Location == CardLocation.MonsterZone || Card.Location == CardLocation.SpellZone) && Card.IsDisabled() && Card.IsFaceup(); + return false; + } + + public bool CheckNumber41(ClientCard card, bool ignoreSelf41 = false) + { + return card != null && card.IsFaceup() && card.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled() + && (!ignoreSelf41 || card.Controller == 0); + } + + /// + /// Whether spell or trap will be negated. If so, return true. + /// + /// is counter trap + /// check target + /// + public bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + // target default set + if (target == null) target = Card; + // won't negate if not on field + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + // negate judge + if (Enemy.HasInMonstersZone(_CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(_CardId.NaturiaBeast, true)) return true; + if (Enemy.HasInSpellZone(_CardId.ImperialOrder, true) || Bot.HasInSpellZone(_CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(_CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(_CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap() && (Enemy.HasInSpellZone(_CardId.RoyalDecreel, true) || Bot.HasInSpellZone(_CardId.RoyalDecreel, true))) return true; + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) return true; + } + // how to get here? + return false; + } + + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + return CheckCardShouldNegate(lastcard); + } + + public bool CheckCardShouldNegate(ClientCard card) + { + if (card == null) return false; + if (card.IsMonster() && card.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (NotToNegateIdList.Contains(card.Id)) return false; + if (card.HasSetcode(_Setcode.Danger) && card.Location == CardLocation.Hand) return false; + if (card.IsMonster() && card.Location == CardLocation.MonsterZone && card.HasPosition(CardPosition.Defence)) + { + if (Enemy.MonsterZone.Any(c => CheckNumber41(c)) || Bot.MonsterZone.Any(c => CheckNumber41(c))) return false; + } + if (DefaultCheckWhetherCardIsNegated(card)) return false; + if (Duel.Player == 1 && card.IsCode(_CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, _CardId.MulcharmyNyalus)) return false; + if (card.IsDisabled()) return false; + + return true; + } + + public bool CheckCardShouldNegate(ChainInfo chainInfo) + { + if (chainInfo == null) return false; + ClientCard card = chainInfo.RelatedCard; + + if (card == null) return false; + if (card.IsMonster() && card.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (NotToNegateIdList.Contains(card.Id)) return false; + if (card.HasSetcode(_Setcode.Danger) && card.Location == CardLocation.Hand) return false; + if (card.IsMonster() && chainInfo.HasLocation(CardLocation.MonsterZone) && chainInfo.HasPosition(CardPosition.Defence)) + { + if (Enemy.MonsterZone.Any(c => CheckNumber41(c)) || Bot.MonsterZone.Any(c => CheckNumber41(c))) return false; + } + if (DefaultCheckWhetherCardIsNegated(card)) return false; + if (Duel.Player == 1 && card.IsCode(_CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, _CardId.MulcharmyNyalus)) return false; + if (card.IsDisabled()) return false; + + return true; + } + + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && (Duel.Player == 0 || Bot.GetMonsterCount() > 0)) return true; + return false; + } + + public bool CheckShouldNoMoreSpSummon() + { + if (CheckAtAdvantage() && enemyActivateMaxxC && !lockBirdSolved && (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2)) + { + return true; + } + return false; + } + + public bool CheckShouldNoMoreSpSummon(CardLocation loc) + { + if (CheckShouldNoMoreSpSummon()) return true; + if (lockBirdSolved || (Duel.Turn > 1 && Duel.Phase < DuelPhase.Main2)) return false; + if (enemyActivatePurulia && (loc & CardLocation.Hand) != 0) return true; + if (enemyActivateFuwalos && (loc & (CardLocation.Deck | CardLocation.Extra)) != 0) return true; + if (enemyActivateNyalus && (loc & (CardLocation.Grave | CardLocation.Removed)) != 0) return true; + + return false; + } + + public bool CheckWhetherCanSummon() + { + return Duel.Player == 0 && Duel.Phase < DuelPhase.End && summonCount > 0; + } + + /// + /// Check whether cards will be removed. If so, do not send cards to grave. + /// + public bool CheckWhetherWillbeRemoved() + { + if (dimensionShifterCount > 0) return true; + List checkIdList = new List { _CardId.BanisheroftheRadiance, _CardId.BanisheroftheLight, _CardId.MacroCosmos, _CardId.DimensionalFissure, + _CardId.KashtiraAriseHeart, _CardId.MaskedHERODarkLaw }; + foreach (int cardid in checkIdList) + { + List fields = new List { Bot, Enemy }; + foreach (ClientField cf in fields) + { + if (cf.HasInMonstersZone(cardid, true, false, true) || cf.HasInSpellZone(cardid, true, true)) + { + return true; + } + } + } + return false; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + ClientCard floodagateCard = Enemy.GetMonsters().Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsFloodgate() && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (floodagateCard != null) return floodagateCard; + + ClientCard dangerCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (dangerCard != null) return dangerCard; + + ClientCard invincibleCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (invincibleCard != null) return invincibleCard; + + ClientCard equippedCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.EquipCards.Count > 0 && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (equippedCard != null) return equippedCard; + + ClientCard enemyExtraMonster = Enemy.MonsterZone.Where(c => c != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && (c.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) || (c.HasType(CardType.Link) && c.LinkCount >= 2)) + && CheckCanBeTargeted(c, canBeTarget, selfType) && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (enemyExtraMonster != null) return enemyExtraMonster; + + ClientCard activatingAlbaz = Enemy.MonsterZone.FirstOrDefault(c => c != null && c.IsCode(68468459) && !c.IsDisabled() + && !currentDestroyCardList.Contains(c) && !currentNegateCardList.Contains(c) && Duel.CurrentChain.Contains(c)); + if (activatingAlbaz != null) return activatingAlbaz; + + if (attack >= 0) + { + if (attack == 0) + attack = Util.GetBestAttack(Bot); + ClientCard betterCard = Enemy.MonsterZone.Where(card => card != null + && card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && CheckCanBeTargeted(card, canBeTarget, selfType) + && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (betterCard != null) return betterCard; + } + return null; + } + + public bool CheckShouldNotIgnore(ClientCard cards, bool ignore = false) + { + return !ignore || (!currentDestroyCardList.Contains(cards) && !currentNegateCardList.Contains(cards)); + } + + public bool CheckCanContinueSummon(bool skipDuodriver = false) + { + bool checkFlag = summonCount > 0 && !activatedCardIdList.Contains(CardId.IceRyzeal) && Bot.HasInHand(CardId.IceRyzeal) + && !DefaultCheckWhetherCardIdIsNegated(CardId.IceRyzeal); + if (Bot.HasInHand(CardId.SwordRyzeal) && !spSummonedCardIdList.Contains(CardId.SwordRyzeal) + && !activatedCardIdList.Contains(CardId.SwordRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.SwordRyzeal)) + { + checkFlag |= Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal)); + checkFlag |= Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal)); + } + checkFlag |= !spSummonedCardIdList.Contains(CardId.ExRyzeal) && !activatedCardIdList.Contains(CardId.ExRyzeal) + && Bot.HasInHand(CardId.ExRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.ExRyzeal) && !CheckWhetherWillbeRemoved(); + + checkFlag |= !activatedCardIdList.Contains(CardId.RyzealDuodrive + 1) && Bot.HasInExtra(CardId.RyzealDuodrive) + && !DefaultCheckWhetherCardIdIsNegated(CardId.RyzealDuodrive) && !CheckWhetherNegated(true, true, CardType.Monster) + && summonCount > 0 && Bot.Hand.Count(c => c.Level == 4) > 0 && GetLevel4CountOnField() == 1 && !lockBirdSolved + && !skipDuodriver; + + return checkFlag; + } + + /// + /// check enemy's dangerous card in grave + /// + public List GetDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantomKnight) || card.HasSetcode(SetcodeHorus))).ToList(); + List dangerMonsterIdList = new List{ + 99937011, 63542003, 9411399, 28954097, 30680659 + }; + result.AddRange(Enemy.Graveyard.GetMatchingCards(card => dangerMonsterIdList.Contains(card.Id))); + return result; + } + + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreSpells = false, CardType selfType = 0) + { + List resultList = new List(); + + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count > 0) resultList.AddRange(floodagateList); + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).ToList(); + if (problemEnemySpellList.Count > 0) resultList.AddRange(ShuffleList(problemEnemySpellList)); + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) resultList.AddRange(dangerList); + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count > 0) resultList.AddRange(invincibleList); + + List enemyMonsters = Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c)).OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count > 0) + { + foreach (ClientCard target in enemyMonsters) + { + if ((target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && !resultList.Contains(target) && CheckCanBeTargeted(target, canBeTarget, selfType) + ) + { + resultList.Add(target); + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !currentDestroyCardList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) && CheckCanBeTargeted(c, canBeTarget, selfType) + && !NotToDestroySpellTrap.Contains(c.Id)).ToList(); + if (spells.Count > 0 && !ignoreSpells) resultList.AddRange(ShuffleList(spells)); + + return resultList; + } + + public List GetNormalEnemyTargetList(bool canBeTarget = true, bool ignoreCurrentDestroy = false, CardType selfType = 0, bool forNegate = false) + { + List targetList = GetProblematicEnemyCardList(canBeTarget, selfType: selfType); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) + && (!forNegate || (!card.IsDisabled() && card.HasType(CardType.Effect))) + ).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && enemyPlaceThisTurn.Contains(card) && card.IsFacedown()).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => + (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && !enemyPlaceThisTurn.Contains(card) && card.IsFacedown()).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) + && (!forNegate || (!card.IsDisabled() && card.HasType(CardType.Effect))) + ).ToList())); + + return targetList; + } + + public List GetMonsterListForTargetNegate(bool canBeTarget = false, CardType selfType = 0) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; + } + + // negate before used + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && CheckCanBeTargeted(card, canBeTarget, selfType) + && !currentNegateCardList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + // negate monster effect on the field + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && CheckCanBeTargeted(chainingCard, canBeTarget, selfType) && !currentNegateCardList.Contains(chainingCard)) + { + if (chainingCard.HasPosition(CardPosition.Defence)) + { + bool have41 = Bot.MonsterZone.Any(c => CheckNumber41(c)) | Enemy.MonsterZone.Any(c => CheckNumber41(c)); + } + resultList.Add(chainingCard); + } + } + + return resultList; + } + + public List GetLevel4OnField(Func filter) + { + return Bot.GetMonsters().Where(c => (filter == null || filter(c)) + && c.IsFaceup() && !c.HasType(CardType.Xyz | CardType.Link) && c.Level == 4).OrderBy(c => c.GetDefensePower()).ToList(); + } + + public int GetLevel4CountOnField() + { + int count = Bot.GetMonsters().Count(c => c.IsFaceup() && !c.HasType(CardType.Xyz | CardType.Link) && c.Level == 4); + return count; + } + + public int GetLevel4FinalCountOnField(bool checkSupport, out bool hasNode) + { + // check whether have 4 monsters for material. + // if not, skip the second xyz monster. + int level4Count = GetLevel4CountOnField(); + if (Bot.HasInHand(CardId.ExRyzeal) && !spSummonedCardIdList.Contains(CardId.ExRyzeal) && !CheckWhetherWillbeRemoved()) + { + if (checkSupport || + (!activatedCardIdList.Contains(CardId.ExRyzeal) && Bot.MonsterZone.All(c => c != null && (c.IsFacedown() || !c.HasType(CardType.Link) && c.Level == 4)))) + { + level4Count++; + } + } + if (Bot.HasInHand(CardId.SwordRyzeal) && !spSummonedCardIdList.Contains(CardId.SwordRyzeal)) + { + bool flag = Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal)); + flag |= Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal)); + if (flag) + { + if (checkSupport || !activatedCardIdList.Contains(CardId.SwordRyzeal)) + { + level4Count++; + } + } + } + hasNode = Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.IsCode(CardId.NodeRyzeal) && !c.IsDisabled()); + if (Bot.HasInHand(CardId.NodeRyzeal) && !spSummonedCardIdList.Contains(CardId.NodeRyzeal)) + { + bool flag = Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasType(CardType.Xyz)); + flag |= Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.HasType(CardType.Xyz)); + if (flag) + { + hasNode = true; + level4Count++; + } + } + if (Bot.HasInHand(CardId.RyzealPlugIn) && !CheckWhetherNegated(true, true, CardType.Spell) && checkSupport) + { + bool flag = false; + List graveTargetList = Bot.Graveyard.Where( + c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && !c.HasType(CardType.Xyz) && c.Level == 4).ToList(); + flag |= graveTargetList.Count() > 0; + hasNode |= graveTargetList.Any(c => c.IsCode(CardId.NodeRyzeal)); + + List banishedTargetList = Bot.Banished.Where( + c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && !c.HasType(CardType.Xyz) && c.Level == 4).ToList(); + flag |= banishedTargetList.Count() > 0; + hasNode |= banishedTargetList.Any(c => c.IsCode(CardId.NodeRyzeal)); + + if (flag) level4Count++; + } + hasNode &= !CheckWhetherWillbeRemoved() && !activatedCardIdList.Contains(CardId.NodeRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.NodeRyzeal); + hasNode &= Bot.Graveyard.Any(c => c != null && c.IsMonster() && c.HasSetcode(SetcodeRyzeal) && !c.IsCode(CardId.NodeRyzeal) && c.Level == 4); + if (hasNode) + { + bool flag = Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && !c.HasType(CardType.Xyz) && c.Level == 4 && !c.IsCode(CardId.NodeRyzeal)); + if (flag) + { + if (checkSupport || GetCostFromHandAndField(null, false).Count() > 0) level4Count++; + } + } + if (checkSupport) + { + int checkHandCount = 2; + if (summonCount > 0 && Bot.Hand.Any(c => c.Level == 4 && !c.IsCode(CardId.ExRyzeal, CardId.SwordRyzeal, CardId.NodeRyzeal))) + { + level4Count++; + checkHandCount++; + } + if (Bot.Hand.Count() >= checkHandCount && Bot.HasInHand(CardId.IceRyzeal) && !spSummonedCardIdList.Contains(CardId.IceRyzeal) && !CheckWhetherWillbeRemoved()) + { + level4Count++; + } + } + + return level4Count; + } + + public List GetCostFromHandAndFieldFirst(ClientCard exceptCard) + { + return Bot.MonsterZone.Where(c => c != null && !c.IsDisabled() && c.IsCode(NeedIceToSolveIdList) && c != exceptCard && !c.HasType(CardType.Token)).ToList(); + } + + public List GetCostFromHandAndField(ClientCard exceptCard, bool sendNotNecessary) + { + List resultList = GetCostFromHandAndFieldFirst(exceptCard); + if (!activatedCardIdList.Contains(CardId.TwinsOfTheEclipse + 1)) + { + List xyzList = Bot.Graveyard.Where(c => c.HasType(CardType.Xyz)).ToList(); + // sending twins + ClientCard twins = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsCode(CardId.TwinsOfTheEclipse) && !resultList.Contains(c)); + if (twins == null) + { + twins = Bot.SpellZone.FirstOrDefault(c => c != null && c.IsCode(CardId.TwinsOfTheEclipse) && !resultList.Contains(c)); + } + if (twins != null) + { + int twinsXyzCount = 0; + foreach (int cardId in twins.Overlays) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null && cardData.HasType(CardType.Xyz)) twinsXyzCount++; + } + bool flag = twinsXyzCount >= 2; + flag |= twinsXyzCount > 0 && xyzList.Count() > 0; + flag |= xyzList.Count() > 1 && xyzList.Count(c => c.IsCanRevive()) > 0; + if (flag) + { + resultList.Add(twins); + } + } + } + + if (Bot.HasInSpellZone(CardId.RyzealCross, true, true)) + { + // sending duodrive because not enough material on field + if (Bot.HasInExtra(CardId.RyzealDuodrive) && !activatedCardIdList.Contains(CardId.RyzealDuodrive + 1) && !lockBirdSolved) + { + bool checkOverlay = true; + ClientCard duoDrive = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsCode(CardId.RyzealDuodrive) && !resultList.Contains(c)); + if (duoDrive == null) + { + checkOverlay = false; + duoDrive = Bot.SpellZone.FirstOrDefault(c => c != null && c.IsCode(CardId.RyzealDuodrive) && !resultList.Contains(c)); + } + if (duoDrive != null) + { + int overlayCount = Bot.MonsterZone.Sum(c => c != null ? c.Overlays.Count() : 0); + if (!checkOverlay || overlayCount < 2) + { + resultList.Add(duoDrive); + } + } + } + // send Detonator with no overlay + if (Bot.HasInExtra(CardId.RyzealDetonator)) + { + ClientCard Detonator = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsCode(CardId.RyzealDetonator) && c.Overlays.Count() == 0 && !resultList.Contains(c)); + if (Detonator != null) + { + resultList.Add(Detonator); + } + } + } + + // sending monsters in spell zone + List monstersInSpellZone = Bot.SpellZone.Where(c => c != null && c.Data != null + && c.Data.HasType(CardType.Monster) && !c.Data.HasType(CardType.Pendulum | CardType.Token) && !resultList.Contains(c)).ToList(); + resultList.AddRange(monstersInSpellZone); + + // send enemy monsters + List enemyMonsters = Bot.MonsterZone.Where(c => c != null && !resultList.Contains(c) && c.Owner == 1).ToList(); + resultList.AddRange(enemyMonsters); + + if (sendNotNecessary) + { + // send xyz monster with no material + List xyzMonsterWithNoMaterial = Bot.MonsterZone.Where( + c => c != null && c.HasType(CardType.Xyz) && c.GetDefensePower() < 2500 && c.Overlays.Count() == 0 && !resultList.Contains(c)) + .OrderBy(c => c.GetDefensePower()).ToList(); + resultList.AddRange(xyzMonsterWithNoMaterial); + + // sending unimportant card in hand + List unimportantList = new List { _CardId.MulcharmyNyalus, _CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, CardId.SeventhTachyon }; + resultList.AddRange(Bot.Hand.Where(c => c != null && c.IsCode(unimportantList) && !resultList.Contains(c))); + + // sending activated ryzeal monster + List checkRyzealIdList = new List { CardId.IceRyzeal, CardId.SwordRyzeal, CardId.ExRyzeal }; + foreach (int checkId in checkRyzealIdList) + { + if (summonCount == 0 && spSummonedCardIdList.Contains(checkId)) + { + List ryzealList = Bot.Hand.Where(c => c != null && c != exceptCard && !resultList.Contains(c) && c.IsCode(checkId)).ToList(); + resultList.AddRange(ryzealList); + } + } + + // sending dump cards + foreach (ClientCard card in Bot.Hand) + { + if (Bot.Hand.Count(c => c != null && !resultList.Contains(c) && c.IsCode(card.Id)) > 1) + { + resultList.Add(card); + } + } + + if (resultList.Count() == 0) + { + // sending other cards + List checkIdList = new List { _CardId.CrossoutDesignator, _CardId.CalledByTheGrave, _CardId.InfiniteImpermanence, + _CardId.GhostOgreAndSnowRabbit, _CardId.LockBird, _CardId.AshBlossom, _CardId.MaxxC }; + foreach (int checkId in checkIdList) + { + List costList = Bot.Hand.Where(c => c != null && c != exceptCard && !resultList.Contains(c) && c.IsCode(checkId)).ToList(); + resultList.AddRange(costList); + } + } + } + + return resultList; + } + + public int GetBotCurrentTotalAttack(List exceptList = null) + { + if (Util.IsTurn1OrMain2() || botSolvedCardIdList.Contains(_CardId.EvilswarmExcitonKnight)) return 0; + int result = 0; + foreach (ClientCard monster in Bot.GetMonsters()) + { + if (exceptList != null && exceptList.Contains(monster)) continue; + if (monster.IsAttack() || !summonThisTurn.Contains(monster)) result += monster.Attack; + } + return result; + } + + public int GetNegateEffectCount() + { + int count = 0; + count += GetCalledbytheGraveIdCount(_CardId.MaxxC) < 2 && Bot.HasInHand(_CardId.MaxxC) ? 1 : 0; + count += GetCalledbytheGraveIdCount(_CardId.AshBlossom) < 2 && Bot.HasInHand(_CardId.AshBlossom) ? 1 : 0; + count += GetCalledbytheGraveIdCount(_CardId.EffectVeiler) < 2 && Bot.HasInHand(_CardId.EffectVeiler) ? 1 : 0; + count += GetCalledbytheGraveIdCount(_CardId.GhostOgreAndSnowRabbit) < 2 && Bot.HasInHand(_CardId.GhostOgreAndSnowRabbit) ? 1 : 0; + count += GetCalledbytheGraveIdCount(_CardId.LockBird) < 2 && Bot.HasInHand(_CardId.LockBird) ? 1 : 0; + count += Bot.SpellZone.Count(c => c != null && c.IsFacedown() && c.IsCode(_CardId.InfiniteImpermanence)); + count += Math.Min(4 - Bot.GetSpellCountWithoutField(), Bot.Hand.Count(c => c.IsCode(_CardId.InfiniteImpermanence))); + + return count; + } + + public override BattlePhaseAction OnBattle(IList attackers, IList defenders) + { + if (attackers.Count() > 0 && defenders.Count() > 0) + { + List sortedAttacker = attackers.OrderBy(card => card.Attack).ToList(); + ClientCard rayLancer = attackers.FirstOrDefault(c => c.IsCode(CardId.FullArmoredUtopicRayLancer) && !c.IsDisabled()); + if (rayLancer != null) + { + sortedAttacker.Remove(rayLancer); + sortedAttacker.Insert(0, rayLancer); + } + for (int k = 0; k < sortedAttacker.Count; ++k) + { + ClientCard attacker = sortedAttacker[k]; + attacker.IsLastAttacker = k == sortedAttacker.Count - 1; + BattlePhaseAction result = OnSelectAttackTarget(attacker, defenders); + if (result != null) + return result; + } + } + + return base.OnBattle(attackers, defenders); + } + + public override ClientCard OnSelectAttacker(IList attackers, IList defenders) + { + + ClientCard twin = attackers.FirstOrDefault(c => c.IsCode(CardId.TwinsOfTheEclipse) && !c.IsDisabled()); + if (twin != null) + { + if (Enemy.MonsterZone.Any(c => c != null && c.GetDefensePower() <= 2500)) + { + return twin; + } + } + return null; + } + + public override void OnSelectChain(IList cards) + { + if (cards != null && cards.Count() > 0) + { + currentCanActivateEffect.Clear(); + currentCanActivateEffect.AddRange(cards); + } + base.OnSelectChain(cards); + } + + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + ChainInfo currentSolvingChain = Duel.GetCurrentSolvingChainInfo(); + ClientCard solvingCard = currentSolvingChain?.RelatedCard; + string cardOptionsText = cards == null ? "null": string.Join(" | ", cards.Select(c => c == null + ? "null" : $"Id={c.Id}, Name={(c.Name ?? "Unknown")}, Loc={c.Location},")); + + int solvingPlayer = currentSolvingChain?.ActivatePlayer ?? Duel.LastChainPlayer; + if (currentSolvingChain != null) + { + if (botSolvingCross) + { + if (hint == HintMsg.DeattachFrom) + { + List> funcList = new List> + { + (c) => c.IsDisabled() && c.IsCode(CardId.RyzealDuodrive), + (c) => c.IsCode(CardId.RyzealDuodrive), + (c) => c.IsDisabled() && c.IsCode(CardId.RyzealDetonator), + (c) => c.IsCode(CardId.RyzealDetonator) + }; + foreach (Func func in funcList) + { + ClientCard target = cards.FirstOrDefault(c => c != null && func(c)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + + if (hint == HintMsg.RemoveXyz) + { + List targets = cards.OrderBy(c => c.Attack).ToList(); + botSolvingCross = false; + return Util.CheckSelectCount(targets, cards, min, max); + } + } + + if (solvingPlayer == 1 && solvingCard.IsCode(_CardId.EvenlyMatched)) + { + Logger.DebugWriteLine("=== Evenly Matched activated."); + List banishList = new List(); + List botMonsters = Bot.GetMonsters().Where(card => !card.HasType(CardType.Token)).ToList(); + + // monster + List faceDownMonsters = botMonsters.Where(card => card.IsFacedown()).ToList(); + banishList.AddRange(faceDownMonsters); + List dumpMainMonsterList = botMonsters.Where(card => !banishList.Contains(card) + && CheckRemainInDeck(card.Id) > 0).ToList(); + dumpMainMonsterList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(dumpMainMonsterList); + + // spells + bool canUsePluginToSpSummonDetonator = Bot.Graveyard.Any(c => c != null && c.IsCanRevive() && c.IsCode(CardId.RyzealDetonator)); + canUsePluginToSpSummonDetonator |= Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDetonator)); + + List faceUpSpells = Bot.GetSpells().Where(c => c.IsFaceup()).ToList(); + banishList.AddRange(ShuffleList(faceUpSpells)); + List faceDownSpells = Bot.GetSpells().Where(c => c.IsFacedown() + && !(canUsePluginToSpSummonDetonator && c.IsCode(CardId.RyzealPlugIn))).ToList(); + banishList.AddRange(ShuffleList(faceDownSpells)); + + List uniqueMainMonster = botMonsters.Where(card => !banishList.Contains(card) + && !card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && CheckRemainInDeck(card.Id) == 0).ToList(); + uniqueMainMonster.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(uniqueMainMonster); + + List dumpExtraMonsterList = botMonsters.Where(card => !banishList.Contains(card) + && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && Bot.HasInExtra(card.Id)).ToList(); + dumpExtraMonsterList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(dumpExtraMonsterList); + + List uniqueExtraMonsterList = botMonsters.Where(card => !banishList.Contains(card) + && card.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz | CardType.Link) && !Bot.HasInExtra(card.Id)).ToList(); + uniqueExtraMonsterList.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(uniqueExtraMonsterList); + + return Util.CheckSelectCount(banishList, cards, min, max); + } + + if (currentSolvingChain.ActivatePlayer == 0) + { + if (hint == HintMsg.AddToHand) + { + if (solvingCard.IsCode(CardId.SwordRyzeal)) + { + ClientCard ice = cards.FirstOrDefault(c => c.IsCode(CardId.IceRyzeal)); + ClientCard ex = cards.FirstOrDefault(c => c.IsCode(CardId.ExRyzeal)); + if (ice != null) + { + bool canSummonAndActivateIce = Duel.Player == 0 && summonCount > 0 && Duel.Phase < DuelPhase.End; + bool flag = canSummonAndActivateIce && !Bot.HasInHand(CardId.IceRyzeal) + && !activatedCardIdList.Contains(CardId.IceRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.IceRyzeal); + flag |= ex == null; + flag |= Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled() && c.IsCode(NeedIceToSolveIdList)) + && !spSummonedCardIdList.Contains(CardId.IceRyzeal) && !CheckWhetherWillbeRemoved(); + if (!canSummonAndActivateIce) + { + flag |= DefaultCheckWhetherCardIdIsNegated(CardId.ExRyzeal); + flag |= spSummonedCardIdList.Contains(CardId.ExRyzeal) || activatedCardIdList.Contains(CardId.ExRyzeal); + } + if (flag) + { + return Util.CheckSelectCount(new List { ice }, cards, min, max); + } + } + if (ex != null) + { + bool flag = !Bot.HasInHand(CardId.ExRyzeal) && !spSummonedCardIdList.Contains(CardId.ExRyzeal) && !activatedCardIdList.Contains(CardId.ExRyzeal); + flag |= Bot.HasInHand(CardId.IceRyzeal); + flag |= ice == null; + if (flag) + { + return Util.CheckSelectCount(new List { ex }, cards, min, max); + } + } + } + + if (solvingCard.IsCode(CardId.ExRyzeal)) + { + ClientCard sword = cards.FirstOrDefault(c => c.IsCode(CardId.SwordRyzeal)); + ClientCard node = cards.FirstOrDefault(c => c.IsCode(CardId.NodeRyzeal)); + if (sword != null) + { + bool flag = node == null; + flag |= !Bot.HasInHand(CardId.SwordRyzeal) && !spSummonedCardIdList.Contains(CardId.SwordRyzeal) && !activatedCardIdList.Contains(CardId.SwordRyzeal); + + if (flag) + { + return Util.CheckSelectCount(new List { sword }, cards, min, max); + } + } + if (node != null) + { + bool flag = sword == null; + flag |= spSummonedCardIdList.Contains(CardId.SwordRyzeal) && activatedCardIdList.Contains(CardId.SwordRyzeal) + && !DefaultCheckWhetherCardIdIsNegated(CardId.SwordRyzeal); + flag |= CheckShouldNoMoreSpSummon(CardLocation.Hand) && !CheckShouldNoMoreSpSummon(CardLocation.Grave) + && !spSummonedCardIdList.Contains(CardId.NodeRyzeal); + + if (flag) + { + return Util.CheckSelectCount(new List { node }, cards, min, max); + } + } + } + + if (solvingCard.IsCode(CardId.Bonfire) || solvingCard.IsCode(CardId.SeventhTachyon)) + { + if (!Bot.HasInHand(CardId.ExRyzeal) && !spSummonedCardIdList.Contains(CardId.ExRyzeal) && !CheckWhetherWillbeRemoved()) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(CardId.ExRyzeal)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + if (!Bot.HasInHand(CardId.IceRyzeal)) + { + bool flag = summonCount > 0 && !activatedCardIdList.Contains(CardId.IceRyzeal); + flag |= !spSummonedCardIdList.Contains(CardId.IceRyzeal) && Bot.Hand.Count > 0; + if (flag) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(CardId.IceRyzeal)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + List searchTargetList = new List { CardId.ExRyzeal, CardId.IceRyzeal }; + foreach (int targetId in searchTargetList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(targetId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + + if (solvingCard.IsCode(CardId.RyzealDuodrive)) + { + // search spells + bool spellNegated = CheckWhetherNegated(true, true, CardType.Spell); + if (!spellNegated) + { + ClientCard cross = cards.FirstOrDefault(c => c.IsCode(CardId.RyzealCross)); + if (cross != null) + { + return Util.CheckSelectCount(new List { cross }, cards, min, max); + } + + ClientCard plugin = cards.FirstOrDefault(c => c.IsCode(CardId.RyzealPlugIn)); + if (plugin != null) + { + return Util.CheckSelectCount(new List { plugin }, cards, min, max); + } + } + + // search for spsummon + List>> checkList = new List>> + { + new KeyValuePair>(CardId.IceRyzeal, + () => Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled() && c.IsCode(NeedIceToSolveIdList)) + && !spSummonedCardIdList.Contains(CardId.IceRyzeal) && !CheckWhetherWillbeRemoved() ), + new KeyValuePair>(CardId.ExRyzeal, + () => !spSummonedCardIdList.Contains(CardId.ExRyzeal) && !activatedCardIdList.Contains(CardId.ExRyzeal) + && !DefaultCheckWhetherCardIdIsNegated(CardId.ExRyzeal) && !CheckWhetherWillbeRemoved() ), + new KeyValuePair>(CardId.IceRyzeal, + () => summonCount > 0 && !activatedCardIdList.Contains(CardId.IceRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.IceRyzeal) ), + new KeyValuePair>(CardId.SwordRyzeal, + () => !spSummonedCardIdList.Contains(CardId.SwordRyzeal) && !activatedCardIdList.Contains(CardId.SwordRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.SwordRyzeal) ), + new KeyValuePair>(CardId.NodeRyzeal, + () => !spSummonedCardIdList.Contains(CardId.NodeRyzeal) ) + }; + foreach (KeyValuePair> pair in checkList) + { + bool alreadyInHand = Bot.HasInHand(pair.Key); + bool shouldSearch = pair.Value(); + if (!alreadyInHand && shouldSearch) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(pair.Key)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + + // search not exists + List searchList = new List { CardId.ExRyzeal, CardId.IceRyzeal, CardId.SwordRyzeal, CardId.NodeRyzeal }; + foreach (int id in searchList) + { + if (!Bot.HasInHand(id)) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(id)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + + // random search + return Util.CheckSelectCount(ShuffleList(cards.ToList()), cards, min, max); + } + } + + if (hint == HintMsg.SpSummon) + { + if (solvingCard.IsCode(CardId.IceRyzeal)) + { + ClientCard sword = cards.FirstOrDefault(c => c.IsCode(CardId.SwordRyzeal)); + ClientCard ex = cards.FirstOrDefault(c => c.IsCode(CardId.ExRyzeal)); + ClientCard node = cards.FirstOrDefault(c => c.IsCode(CardId.NodeRyzeal)); + if (sword != null) + { + bool flag = !activatedCardIdList.Contains(CardId.SwordRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.SwordRyzeal); + flag |= Bot.HasInHand(CardId.ExRyzeal) && !spSummonedCardIdList.Contains(CardId.ExRyzeal); + flag |= ex == null && node == null; + if (flag) + { + return Util.CheckSelectCount(new List { sword }, cards, min, max); + } + } + if (ex != null) + { + bool flag = !activatedCardIdList.Contains(CardId.ExRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.ExRyzeal); + flag |= Bot.HasInHand(CardId.SwordRyzeal) && !spSummonedCardIdList.Contains(CardId.SwordRyzeal); + flag |= sword == null && node == null; + if (flag) + { + return Util.CheckSelectCount(new List { ex }, cards, min, max); + } + } + if (node != null) + { + return Util.CheckSelectCount(new List { node }, cards, min, max); + } + } + + if (solvingCard.IsCode(CardId.TwinsOfTheEclipse)) + { + ClientCard target = TwinsOfTheEclipseRebornTarget(new List(cards)); + + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + + if (hint == HintMsg.ToDeck) + { + if (solvingCard.IsCode(CardId.TripleTacticsTalent)) + { + foreach (ClientCard hand in cards) + { + foreach (int setcode in CheckSetcodeList) + { + if (hand.HasSetcode(setcode)) + { + enemyDeckTypeRecord.Add(setcode); + } + } + } + return Util.CheckSelectCount(ShuffleList(cards.ToList()), cards, min, max); + } + } + + if (hint == HintMsg.XyzMaterial) + { + if (solvingCard.IsCode(CardId.RyzealDetonator, CardId.RyzealDuodrive, CardId.RyzealPlugIn)) + { + // material that have effect + ClientCard effectTarget = cards.FirstOrDefault(c => c.IsCode(CardId.TwinsOfTheEclipse, CardId.MereologicAggregator)); + if (effectTarget != null) + { + return Util.CheckSelectCount(new List { effectTarget }, cards, min, max); + } + // cards in hand + foreach (ClientCard card in cards) + { + if (Bot.Hand.Count(c => c.IsCode(card.Id)) > 0) + { + return Util.CheckSelectCount(new List { card }, cards, min, max); + } + } + // dump card + foreach (ClientCard card in cards) + { + if (cards.Count(c => c.IsCode(card.Id)) > 1) { + return Util.CheckSelectCount(new List { card }, cards, min, max); + } + } + } + } + + if (hint == HintMsg.RemoveXyz) + { + if (solvingCard.IsCode(CardId.RyzealDuodrive)) + { + List resultList = new List(); + + List detachOwnerIdList = new List { CardId.StarliegePhotonBlastDragon, CardId.RyzealDuodrive, CardId.TwinsOfTheEclipse, + CardId.FullArmoredUtopicRayLancer, _CardId.EvilswarmExcitonKnight, CardId.Number60DugaresTheTimeless, CardId.RyzealDetonator }; + foreach (int ownerId in detachOwnerIdList) + { + // detach from DuoDrive first + List detachMaterialList = cards.Where(c => c.OwnTargets.Any(oc => oc.IsCode(ownerId))).ToList(); + if (detachMaterialList.Count() > 0) + { + // Detonator + ClientCard Detonator = detachMaterialList.FirstOrDefault(c => c.IsCode(CardId.RyzealDetonator)); + if (Detonator != null) + { + resultList.Add(Detonator); + } + List> filterList = new List> + { + (c) => !resultList.Contains(c) + && !c.IsCode(CardId.MereologicAggregator, CardId.TwinsOfTheEclipse) + && Bot.HasInSpellZone(CardId.RyzealCross) && c.HasSetcode(SetcodeRyzeal) + && (c.Data == null || ((c.Data.Attribute & attrbuteLightDark) != 0)), + (c) => !resultList.Contains(c) + && !c.IsCode(CardId.MereologicAggregator, CardId.TwinsOfTheEclipse) + && Bot.HasInSpellZone(CardId.RyzealCross) && c.HasSetcode(SetcodeRyzeal), + (c) => !resultList.Contains(c) + && !c.IsCode(CardId.MereologicAggregator, CardId.TwinsOfTheEclipse) + && (c.Data == null || ((c.Data.Attribute & attrbuteLightDark) != 0)), + (c) => !resultList.Contains(c) + && !c.IsCode(CardId.MereologicAggregator, CardId.TwinsOfTheEclipse), + (c) => !resultList.Contains(c) + }; + foreach (Func filter in filterList) + { + foreach (ClientCard material in detachMaterialList) + { + if (filter(material)) + { + resultList.Add(material); + } + } + } + } + } + return Util.CheckSelectCount(resultList, cards, min, max); + } + } + + // choose attach material by plugin from Deck + if (solvingCard.IsCode(CardId.RyzealPlugIn) && cards.All(c => c.Location == CardLocation.Deck)) + { + List priority = new List + { + CardId.ExRyzeal, + CardId.IceRyzeal, + CardId.SwordRyzeal, + CardId.NodeRyzeal + }; + foreach (int id in priority) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(id)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + + // gain material by plugin + if (solvingCard.IsCode(CardId.RyzealPlugIn) && cards.All(c => c.Location == CardLocation.MonsterZone)) + { + ClientCard abyssDweller = cards.FirstOrDefault(c => c != null && !c.IsDisabled() && c.IsCode(CardId.AbyssDweller) && c.Overlays.Count() < 2); + if (abyssDweller != null && AbyssDwellerSummonCheck()) + { + return Util.CheckSelectCount(new List { abyssDweller }, cards, min, max); + } + + ClientCard duoDriver = cards.FirstOrDefault(c => c != null && !c.IsDisabled() && c.IsCode(CardId.RyzealDuodrive) && c.Overlays.Count() == 1); + if (duoDriver != null && Bot.HasInMonstersZone(CardId.StarliegePhotonBlastDragon, true, false, true)) + { + return Util.CheckSelectCount(new List { duoDriver }, cards, min, max); + } + + ClientCard Detonator = cards.FirstOrDefault(c => c != null && !c.IsDisabled() && c.IsCode(CardId.RyzealDetonator)); + if (Detonator != null) + { + return Util.CheckSelectCount(new List { Detonator }, cards, min, max); + } + + if (Bot.HasInSpellZone(CardId.RyzealCross, true, true)) + { + ClientCard ryzealXyz = cards.FirstOrDefault(c => c != null && c.HasSetcode(SetcodeRyzeal)); + if (ryzealXyz != null) + { + return Util.CheckSelectCount(new List { ryzealXyz }, cards, min, max); + } + } + + ClientCard tornadoDragon = cards.FirstOrDefault(c => c != null && !c.IsDisabled() && c.IsCode(CardId.TornadoDragon) && c.Overlays.Count() == 1); + if (tornadoDragon != null && TornadoDragonSummonCheck()) + { + return Util.CheckSelectCount(new List { tornadoDragon }, cards, min, max); + } + + ClientCard no41 = cards.FirstOrDefault(c => c != null && c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir)); + if (no41 != null && Number41BagooskatheTerriblyTiredTapirSummonCheck()) + { + return Util.CheckSelectCount(new List { no41 }, cards, min, max); + } + + duoDriver = cards.FirstOrDefault(c => c != null && !c.IsDisabled() && c.IsCode(CardId.RyzealDuodrive)); + if (duoDriver != null) + { + return Util.CheckSelectCount(new List { duoDriver }, cards, min, max); + } + } + + // double attack + if (solvingCard.IsCode(CardId.Number60DugaresTheTimeless) && cards.All(c => c.Location == CardLocation.MonsterZone)) + { + ClientCard maxAttackMonster = cards.Where(c => c != null && (c.HasPosition(CardPosition.FaceUpAttack) || !summonThisTurn.Contains(c))) + .OrderByDescending(c => c.Attack).FirstOrDefault(); + if (maxAttackMonster != null) + { + return Util.CheckSelectCount(new List { maxAttackMonster }, cards, min, max); + } + } + } + + // hand solve + if (hint == HintMsg.ToDeck || hint == HintMsg.ToGrave || hint == HintMsg.Discard) + { + bool allBotHand = cards.All(c => c.Controller == 0 && c.Location == CardLocation.Hand); + if (allBotHand) + { + List resultList = new List(); + List returnList = new List { _CardId.MulcharmyNyalus, _CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, + CardId.SeventhTachyon + }; + foreach (int code in returnList) + { + List targetList = cards.Where(c => c.IsCode(code) && !resultList.Contains(c)).ToList(); + if (targetList.Count() > 0) + { + resultList.AddRange(targetList); + } + } + // return dump card + foreach (ClientCard card in cards) + { + if (!resultList.Contains(card) && cards.Where(c => c.IsCode(card.Id) && !resultList.Contains(c)).Count() > 1) + { + resultList.Add(card); + } + } + List unproperCardList = new List { _CardId.EffectVeiler, _CardId.InfiniteImpermanence, _CardId.GhostOgreAndSnowRabbit, CardId.TripleTacticsTalent, + CardId.NodeRyzeal, _CardId.LockBird, CardId.RyzealPlugIn, _CardId.CrossoutDesignator, _CardId.CalledByTheGrave, CardId.RyzealCross, + CardId.SwordRyzeal, CardId.ExRyzeal, CardId.IceRyzeal + }; + foreach (int code in unproperCardList) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(code) && !resultList.Contains(c)); + if (target != null) + { + resultList.Add(target); + } + } + return Util.CheckSelectCount(resultList, cards, min, max); + } + } + } + + if (solvingCard == null) + { + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null) + { + // handle for RyzealDetonator + if (lastChainCard.Controller == 0 && lastChainCard.IsCode(CardId.RyzealDetonator)) + { + if (hint == HintMsg.RemoveXyz) + { + if (DetonatorDestroySelf != null) + { + ClientCard detachTarget = cards.FirstOrDefault(c => c.IsCode(CardId.MereologicAggregator, CardId.TwinsOfTheEclipse)); + if (detachTarget != null) + { + return Util.CheckSelectCount(new List { detachTarget }, cards, min, max); + } + } + List targets = cards.OrderBy(c => c.Attack).ToList(); + return Util.CheckSelectCount(targets, cards, min, max); + } else if (hint == HintMsg.Destroy) + { + if (DetonatorDestroySelf != null && cards.Contains(DetonatorDestroySelf)) + { + return Util.CheckSelectCount(new List { DetonatorDestroySelf }, cards, min, max); + } + List targetList = CanDestroyList(); + foreach (ClientCard target in targetList) + { + if (cards.Contains(target)) + { + currentDestroyCardList.Add(target); + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + } + + if (hint == HintMsg.RemoveXyz && Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn)) + { + List checkRyzealIdList = new List { CardId.NodeRyzeal, CardId.SwordRyzeal, CardId.ExRyzeal }; + foreach (int checkId in checkRyzealIdList) + { + if (!activatedCardIdList.Contains(checkId)) + { + ClientCard target = cards.FirstOrDefault(c => c.IsCode(checkId)); + if (target != null) + { + return Util.CheckSelectCount(new List { target }, cards, min, max); + } + } + } + } + } + } + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + /// + /// go first + /// + public override bool OnSelectHand() + { + HashSet tenpaiList = new HashSet { SetcodeTenpaiDragon, SetcodeSangen }; + bool maybeTenpai = enemyDeckTypeRecord.Count() > 0 && enemyDeckTypeRecord.All(c => tenpaiList.Contains(c)); + return !maybeTenpai; + } + + public override int OnSelectOption(IList options) + { + bool tripleCheck = false; + for (int opt = 0; opt < 3; ++ opt) + { + if (options.Contains(Util.GetStringId(CardId.TripleTacticsTalent, opt))) + { + tripleCheck = true; + break; + } + } + if (tripleCheck) + { + return TripleTacticsTalentDecision(options); + } + + bool no60Check = false; + for (int opt = 0; opt < 3; ++ opt) + { + if (options.Contains(Util.GetStringId(CardId.Number60DugaresTheTimeless, opt))) + { + no60Check = true; + break; + } + } + if (no60Check) + { + // double attack + if (options.Contains(Util.GetStringId(CardId.Number60DugaresTheTimeless, 2))) + { + if (Number60DugaresTheTimelessDoubleTarget() != null) + { + int res = options.IndexOf(Util.GetStringId(CardId.Number60DugaresTheTimeless, 2)); + if (res >= 0) + { + return res; + } + } + } + // draw effect + if (options.Contains(Util.GetStringId(CardId.Number60DugaresTheTimeless, 0))) + { + if (Number60DugaresTheTimelessDrawEffect()) + { + int res = options.IndexOf(Util.GetStringId(CardId.Number60DugaresTheTimeless, 0)); + if (res >= 0) + { + return res; + } + } + } + // reborn + if (options.Contains(Util.GetStringId(CardId.Number60DugaresTheTimeless, 1))) + { + if (Number60DugaresTheTimelessRebornEffect()) + { + int res = options.IndexOf(Util.GetStringId(CardId.Number60DugaresTheTimeless, 1)); + if (res >= 0) + { + return res; + } + } + } + } + + ClientCard currentSolvingChain = Duel.GetCurrentSolvingChainCard(); + if (currentSolvingChain != null) + { + // TODO + } + + return base.OnSelectOption(options); + } + + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone) + { + List zoneIdList = ShuffleList(new List { 5, 6 }); + zoneIdList.AddRange(ShuffleList(new List { 0, 2, 4 })); + zoneIdList.AddRange(ShuffleList(new List { 1, 3 })); + foreach (int zoneId in zoneIdList) + { + int zone = (int)System.Math.Pow(2, zoneId); + if ((available & zone) != 0 && Bot.MonsterZone[zoneId] == null) + { + return zone; + } + } + } + return base.OnSelectPlace(cardId, player, location, available); + } + + public override bool OnSelectYesNo(long desc) + { + if (desc == Util.GetStringId(CardId.RyzealPlugIn, 1)) + { + Logger.DebugWriteLine("** plugin set material"); + return true; + } + + return base.OnSelectYesNo(desc); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + if (cardId == _CardId.Number41BagooskatheTerriblyTiredTapir && (Util.IsTurn1OrMain2() || Duel.Player == 1)) + { + return CardPosition.FaceUpDefence; + } + + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (Duel.Turn == 1 || Duel.Phase >= DuelPhase.Main2) + { + bool turnDefense = false; + if (cardData.Attack <= cardData.Defense) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } + int cardAttack = cardData.Attack; + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardAttack); + + if (Bot.HasInExtra(CardId.Number60DugaresTheTimeless) && GetLevel4FinalCountOnField(true, out _) >= 2) + { + bestBotAttack *= 2; + } + + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } + + public override void OnNewTurn() + { + if (Duel.Turn <= 1) + { + dimensionShifterCount = 0; + // for doom bot + maxSummonCount = 1; + hardToDestroyCardList.Clear(); + cannotDestroyCardList.Clear(); + } + + summonCount = maxSummonCount; + enemyActivateMaxxC = false; + enemyActivatePurulia = false; + enemyActivateFuwalos = false; + enemyActivateNyalus = false; + lockBirdSolved = false; + if (dimensionShifterCount > 0) dimensionShifterCount--; + enemyActivateInfiniteImpermanenceFromHand = false; + botActivateMulcharmy = false; + DetonatorDestroySelf = null; + botSolvingCross = false; + infiniteImpermanenceList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + activatedCardIdList.Clear(); + spSummonedCardIdList.Clear(); + botSolvedCardIdList.Clear(); + enemyPlaceThisTurn.Clear(); + summonThisTurn.Clear(); + currentCanActivateEffect.Clear(); + base.OnNewTurn(); + } + + public override void OnChaining(int player, ClientCard card) + { + Duel.LastChainTargets.Clear(); + if (card == null) return; + + if (player == 1) + { + if (card.IsCode(_CardId.InfiniteImpermanence)) + { + if (enemyActivateInfiniteImpermanenceFromHand) + { + enemyActivateInfiniteImpermanenceFromHand = false; + } + else + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + } + + if (card.HasSetcode(SetcodeFloowandereeze)) + { + enemyDeckTypeRecord.Add(SetcodeFloowandereeze); + } + } + + if (player == 0) + { + if (card.IsCode(_CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, _CardId.MulcharmyNyalus)) + { + botActivateMulcharmy = true; + } + } + base.OnChaining(player, card); + } + + public override void OnChainSolved(int chainIndex) + { + botSolvingCross = false; + ChainInfo currentChain = Duel.GetCurrentSolvingChainInfo(); + if (currentChain != null && !Duel.IsCurrentSolvingChainNegated()) + { + if (!Duel.IsCurrentSolvingChainNegated()) + { + if (currentChain.IsCode(_CardId.LockBird)) + lockBirdSolved = true; + if (currentChain.IsCode(_CardId.DimensionShifter)) + dimensionShifterCount = 2; + if (currentChain.ActivatePlayer == 1) + { + if (currentChain.IsCode(_CardId.MaxxC)) + enemyActivateMaxxC = true; + if (currentChain.IsCode(_CardId.MulcharmyPurulia)) + enemyActivatePurulia = true; + if (currentChain.IsCode(_CardId.MulcharmyFuwalos)) + enemyActivateFuwalos = true; + if (currentChain.IsCode(_CardId.MulcharmyNyalus)) + enemyActivateNyalus = true; + } + if (currentChain.ActivatePlayer == 0) + { + foreach (int checkId in CheckBotSolvedList) + { + if (currentChain.IsCode(checkId)) + { + botSolvedCardIdList.Add(checkId); + } + } + } + } + } + + base.OnChainSolved(chainIndex); + } + + public override void OnChainEnd() + { + for (int idx = cannotDestroyCardList.Count - 1; idx >= 0; idx--) + { + ClientCard checkTarget = cannotDestroyCardList[idx]; + if (checkTarget == null || !checkTarget.IsOnField()) + { + cannotDestroyCardList.RemoveAt(idx); + } + } + for (int idx = hardToDestroyCardList.Count - 1; idx >= 0; idx--) + { + ClientCard checkTarget = hardToDestroyCardList[idx]; + if (checkTarget == null || !checkTarget.IsOnField()) + { + hardToDestroyCardList.RemoveAt(idx); + } + } + foreach (ClientCard card in currentDestroyCardList) + { + if (card != null && card.IsOnField()) + { + if (hardToDestroyCardList.Contains(card)) + { + cannotDestroyCardList.Add(card); + } else + { + hardToDestroyCardList.Add(card); + } + } + } + + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + currentCanActivateEffect.Clear(); + enemyActivateInfiniteImpermanenceFromHand = false; + botSolvingCross = false; + DetonatorDestroySelf = null; + for (int idx = enemyPlaceThisTurn.Count - 1; idx >= 0; idx--) + { + ClientCard checkTarget = enemyPlaceThisTurn[idx]; + if (checkTarget == null || !checkTarget.IsOnField()) + { + enemyPlaceThisTurn.RemoveAt(idx); + } + } + base.OnChainEnd(); + } + + public override void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + if (card != null) + { + if (previousControler == 1) + { + if (card.IsCode(_CardId.InfiniteImpermanence) && previousLocation == (int)CardLocation.Hand && currentLocation == (int)CardLocation.SpellZone) + enemyActivateInfiniteImpermanenceFromHand = true; + } + if (card.Owner == 1) + { + foreach (int setcode in CheckSetcodeList) + { + if (card.HasSetcode(setcode)) + { + enemyDeckTypeRecord.Add(setcode); + } + } + if (card.IsCode(AlbazFusionList)) + { + enemyDeckTypeRecord.Add(SetcodeBranded); + } + } + if (currentControler == 1 && (currentLocation == (int)CardLocation.MonsterZone || currentLocation == (int)CardLocation.SpellZone)) + { + enemyPlaceThisTurn.Add(card); + } + + if (previousControler == 0 && previousLocation == (int)CardLocation.MonsterZone && currentLocation != (int)CardLocation.MonsterZone && summonThisTurn.Contains(card)) + { + summonThisTurn.Remove(card); + } + if (currentControler == 0 && currentLocation == (int)CardLocation.MonsterZone) + { + summonThisTurn.Add(card); + } + } + + base.OnMove(card, previousControler, previousLocation, currentControler, currentLocation); + } + + public override void OnSpSummoned() + { + // not special summoned by chain + if (Duel.GetCurrentSolvingChainCard() == null) + { + foreach (ClientCard card in Duel.LastSummonedCards) + { + if (card.Controller == 0 && card.IsCode(CardId.IceRyzeal, CardId.SwordRyzeal, CardId.NodeRyzeal, CardId.ExRyzeal)) + { + spSummonedCardIdList.Add(card.GetOriginCode()); + } + } + } + } + + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public bool IceRyzealSpSummonFirst() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Hand | CardLocation.Deck | CardLocation.Extra)) + { + bool flag = Bot.GetMonsters().Any(c => c.IsFaceup() && c.HasType(CardType.Xyz)); + flag |= GetLevel4CountOnField() >= 2; + if (flag) return false; + } + List costList = GetCostFromHandAndFieldFirst(Card); + if (costList.Count() > 0) + { + AI.SelectCard(costList); + return true; + } + + return false; + } + + public bool IceRyzealSpSummon() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Hand | CardLocation.Deck | CardLocation.Extra)) + { + bool flag = Bot.GetMonsters().Any(c => c.IsFaceup() && c.HasType(CardType.Xyz)); + flag |= GetLevel4CountOnField() >= 2; + if (flag) return false; + } + if (Card.Level != 4) return false; + if (summonCount <= 0 && GetLevel4CountOnField() == 1) + { + List firstCostList = GetCostFromHandAndField(Card, false); + if (firstCostList.Count() > 0) + { + AI.SelectCard(firstCostList); + return true; + } + if (Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && !c.IsDisabled() && c.IsFloodgate()) || !CheckWhetherHaveFinalMonster()) + { + List costList = GetCostFromHandAndField(Card, true); + if (costList.Count() > 0) + { + AI.SelectCard(costList); + return true; + } + } + } + + return false; + } + + public bool IceRyzealSummon() + { + if (CheckWhetherNegated(true, true)) return false; + if (CheckShouldNoMoreSpSummon(CardLocation.Hand | CardLocation.Deck | CardLocation.Extra)) + { + bool flag = Bot.GetMonsters().Any(c => c.IsFaceup() && c.HasType(CardType.Xyz)); + flag |= GetLevel4CountOnField() >= 2; + if (flag) return false; + } + summonCount -= 1; + return true; + } + + public bool IceRyzealActivate() + { + if (CheckWhetherNegated(true)) return false; + if (CheckShouldNoMoreSpSummon(CardLocation.Deck) && GetLevel4CountOnField() >= 2) + { + return false; + } + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool SwordRyzealSpSummon() + { + int lv4Count = GetLevel4CountOnField(); + if (CheckShouldNoMoreSpSummon(CardLocation.Hand | CardLocation.Deck | CardLocation.Extra)) + { + bool flag = Bot.GetMonsters().Any(c => c.IsFaceup() && c.HasType(CardType.Xyz)); + flag |= lv4Count >= 2; + flag |= lv4Count == 1 && summonCount > 0; + if (flag) return false; + } + bool spsummonFlag = lv4Count == 1; + spsummonFlag |= !CheckWhetherNegated(true, true, CardType.Monster) && CheckRemainInDeck(CardId.IceRyzeal, CardId.ExRyzeal) > 0 + && !activatedCardIdList.Contains(CardId.SwordRyzeal) && !lockBirdSolved; + if (GetLevel4CountOnField() == 0) + { + spsummonFlag |= GetLevel4FinalCountOnField(true, out _) >= 2 && !CheckWhetherHaveFinalMonster(); + } + + return spsummonFlag; + } + + public bool SwordRyzealSummon() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Extra)) + { + int lv4Count = GetLevel4CountOnField(); + if (lv4Count == 1) + { + bool flag = !activatedCardIdList.Contains(CardId.SwordRyzeal); + flag &= !(Bot.HasInHand(CardId.ExRyzeal) && !activatedCardIdList.Contains(CardId.ExRyzeal)); + if (flag) + { + summonCount -= 1; + return true; + } + } + bool skipFlag = Bot.GetMonsters().Any(c => c.IsFaceup() && c.HasType(CardType.Xyz)); + skipFlag |= lv4Count >= 2; + if (skipFlag) return false; + } + if (CheckWhetherNegated(true)) return false; + + summonCount -= 1; + return true; + } + + public bool SwordRyzealActivate() + { + if (CheckWhetherNegated(true)) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool NodeRyzealSpSummon() + { + int lv4Count = GetLevel4CountOnField(); + if (CheckShouldNoMoreSpSummon(CardLocation.Hand | CardLocation.Deck | CardLocation.Extra)) + { + bool flag = Bot.GetMonsters().Any(c => c.IsFaceup() && c.HasType(CardType.Xyz)); + flag |= lv4Count >= 2; + flag |= lv4Count == 1 && summonCount > 0; + if (flag) return false; + } + bool spsummonFlag = lv4Count == 1; + spsummonFlag |= !CheckWhetherNegated(true, true, CardType.Monster) && Bot.Graveyard.Any(c => !c.HasType(CardType.Xyz) && c.HasSetcode(SetcodeRyzeal) && c.Level == 4); + return spsummonFlag; + } + + public bool NodeRyzealSpSummonFirst() + { + int lv4Count = GetLevel4CountOnField(); + if (CheckShouldNoMoreSpSummon(CardLocation.Hand | CardLocation.Deck | CardLocation.Extra)) + { + bool flag = Bot.GetMonsters().Any(c => c.IsFaceup() && c.HasType(CardType.Xyz)); + flag |= lv4Count >= 2; + flag |= lv4Count == 1 && summonCount > 0; + if (flag) return false; + } + if (!activatedCardIdList.Contains(Card.Id) && GetCostFromHandAndField(Card, false).Count() > 0) + { + return true; + } + return false; + } + + public bool NodeRyzealActivate() + { + if (NodeRyzealActivateInner(true)) + { + return true; + } + + return false; + } + + public bool NodeRyzealActivateFirst() + { + if (NodeRyzealActivateInner(false)) + { + return true; + } + + return false; + } + + public bool NodeRyzealActivateInner(bool sendNotNessary) + { + if (CheckWhetherNegated(true)) return false; + if (CheckShouldNoMoreSpSummon(CardLocation.Grave | CardLocation.Extra)) + { + if (Bot.GetMonsters().Count(c => c.HasType(CardType.Xyz) && c.IsFaceup()) > 0) + { + return false; + } + } + List fireIdList = new List { CardId.NodeRyzeal, CardId.SwordRyzeal }; + List lightIdList = new List { CardId.ExRyzeal, CardId.IceRyzeal }; + List rebornTarget = new List(); + foreach (int id in fireIdList) + { + ClientCard target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsCode(id)); + if (target != null && !rebornTarget.Contains(target)) rebornTarget.Add(target); + if (rebornTarget.Count == 2) break; + } + foreach (int id in lightIdList) + { + ClientCard target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsCode(id)); + if (target != null && !rebornTarget.Contains(target)) rebornTarget.Add(target); + if (rebornTarget.Count == 2) break; + } + if (rebornTarget.Count == 0) return false; + + if (sendNotNessary) + { + ClientCard twinsOnField = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsCode(CardId.TwinsOfTheEclipse)); + + // Check if we'd have enough XYZ summons available if we send Twins + int currentLevel4 = GetLevel4FinalCountOnField(true, out _); + int xyzCount = Bot.MonsterZone.Count(c => c != null && c.IsFaceup() && c.HasType(CardType.Xyz) && c.Rank == 4); + + if ((twinsOnField != null) && currentLevel4 >= 2 && xyzCount < 3) + { + List reborn = new List { twinsOnField }; + + List costList = GetCostFromHandAndField(Card, true); + if (costList.Count() > 0) + { + AI.SelectCard(costList); + AI.SelectNextCard(reborn); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + List firstCostList = GetCostFromHandAndField(Card, false); + if (firstCostList.Count() > 0) + { + AI.SelectCard(firstCostList); + AI.SelectNextCard(rebornTarget); + activatedCardIdList.Add(Card.Id); + return true; + } + + if (GetLevel4CountOnField() == 1 && sendNotNessary) + { + List nextCostList = GetCostFromHandAndField(Card, true); + if (nextCostList.Count() > 0) + { + AI.SelectCard(nextCostList); + AI.SelectNextCard(rebornTarget); + activatedCardIdList.Add(Card.Id); + return true; + } + } + return false; + } + + public bool ExRyzealSpSummon() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Hand | CardLocation.Extra)) + { + bool checkFlag = !CheckWhetherHaveFinalMonster() && GetLevel4CountOnField() == 1; + if (checkFlag && ExRyzealDiscardExtra()) + { + return true; + } + return false; + } + if (Duel.Turn == 1) + { + bool checkFlag = !activatedCardIdList.Contains(CardId.ExRyzeal) && !lockBirdSolved && !DefaultCheckWhetherCardIdIsNegated(CardId.ExRyzeal) && !Bot.HasInMonstersZone(_CardId.Number41BagooskatheTerriblyTiredTapir); + checkFlag |= !Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasType(CardType.Xyz)) && GetLevel4CountOnField() == 1; + if (checkFlag) + { + // discard ryzeal + AI.SelectCard(CardId.RyzealDetonator, CardId.RyzealDuodrive); + return true; + } + } + if (ExRyzealDiscardExtra()) + { + return true; + } + + return false; + } + + public bool ExRyzealDiscardExtra() + { + List discardIdCheckList = new List { CardId.MereologicAggregator, CardId.TwinsOfTheEclipse, CardId.Number104Masquerade, CardId.StarliegePhotonBlastDragon, + CardId.TornadoDragon, CardId.AbyssDweller, _CardId.EvilswarmExcitonKnight, CardId.FullArmoredUtopicRayLancer, CardId.Number60DugaresTheTimeless, + CardId.RyzealDuodrive, CardId.RyzealDetonator}; + + // delay id + List discardIdList = new List(); + foreach (int discardId in discardIdCheckList) + { + if (discardId == CardId.MereologicAggregator) + { + if (!Enemy.MonsterZone.Any(c => c != null && c.IsFaceup() && !c.IsDisabled() && !c.IsShouldNotBeMonsterTarget()) + && Enemy.SpellZone.Any(c => c != null && c.IsFaceup() && !c.IsDisabled() && !c.IsShouldNotBeMonsterTarget())) + { + continue; + } + } + if (discardId == CardId.TwinsOfTheEclipse) + { + if (Bot.Graveyard.Count(c => c.HasType(CardType.Xyz)) < 2 || !Bot.Graveyard.Any(c => c.HasType(CardType.Xyz) && c.IsCanRevive())) + { + continue; + } + } + if (discardId == CardId.Number104Masquerade) + { + if (CheckRemainInDeck(CardId.SeventhTachyon) > 0 || Bot.HasInHandOrInSpellZone(CardId.Number104Masquerade)) + { + continue; + } + } + if (discardId == CardId.TornadoDragon) + { + if (Enemy.GetSpellCount() > 0) + { + continue; + } + } + discardIdList.Add(discardId); + } + discardIdList.AddRange(discardIdCheckList); + + foreach (int id in discardIdList) + { + if (Bot.HasInExtra(id)) + { + AI.SelectCard(id); + return true; + } + } + + return false; + } + + public bool ExRyzealSpSummonLater() + { + // TODO + return false; + } + + public bool ExRyzealSummon() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Extra)) + { + if (GetLevel4CountOnField() == 1) + { + bool flag = !activatedCardIdList.Contains(CardId.ExRyzeal); + flag &= !(Bot.HasInHand(CardId.SwordRyzeal) && !activatedCardIdList.Contains(CardId.SwordRyzeal)); + if (flag) + { + summonCount -= 1; + return true; + } + } + } + return false; + } + + public bool ExRyzealActivate() + { + if (CheckWhetherNegated(true)) + { + // use mero to negate No.41? + int count41 = Enemy.GetMonsters().Count(c => c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir) && c.IsFaceup() + && !c.IsDisabled() && c.HasPosition(CardPosition.FaceUpDefence)); + bool canNegate41 = count41 == 1 && currentCanActivateEffect.Any(c => c != null && c.IsCode(CardId.MereologicAggregator)); + + if (canNegate41) + { + activatedCardIdList.Add(Card.Id); + return true; + } else + { + return false; + } + } + activatedCardIdList.Add(Card.Id); + return true; + } + + public bool MulcharmyFuwalosActivate() + { + if (CheckWhetherNegated(true) || Duel.Player == 0) return false; + if (lockBirdSolved || Duel.CurrentChain.Any(c => c.IsCode(_CardId.LockBird))) return false; + if (Duel.Phase > DuelPhase.Main1) return false; + + botActivateMulcharmy = true; + return true; + } + + public bool MulcharmyPuruliaActivate() + { + if (CheckWhetherNegated(true) || Duel.Player == 0) return false; + if (lockBirdSolved || Duel.CurrentChain.Any(c => c.IsCode(_CardId.LockBird))) return false; + if (Duel.Phase > DuelPhase.Main1) return false; + if (botActivateMulcharmy) return false; + + botActivateMulcharmy = true; + return true; + } + + public bool MulcharmyNyalusActivate() + { + if (CheckWhetherNegated(true) || Duel.Player == 0) return false; + if (lockBirdSolved || Duel.CurrentChain.Any(c => c.IsCode(_CardId.LockBird))) return false; + if (Duel.Phase > DuelPhase.Main1) return false; + if (botActivateMulcharmy) return false; + + botActivateMulcharmy = true; + return true; + } + + public bool AshBlossomActivate() + { + if (CheckWhetherNegated(true) || !CheckLastChainShouldNegated()) return false; + if (DefaultAshBlossomAndJoyousSpring()) + { + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard.Location == CardLocation.MonsterZone || lastChainCard.Location == CardLocation.SpellZone) currentNegateCardList.Add(Util.GetLastChainCard()); + return true; + } + return false; + } + + public bool GhostOgreAndSnowRabbitActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard == null || lastChainCard.IsDisabled()) return false; + if (lastChainCard.IsMonster() && !lastChainCard.HasType(CardType.Link | CardType.Fusion | CardType.Synchro | CardType.Xyz)) return false; + return true; + } + + public bool MaxxCActivate() + { + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0 || lockBirdSolved) return false; + return DefaultMaxxC(); + } + + public bool LockBirdActivate() + { + if (CheckWhetherNegated(true)) return false; + if (Duel.Player == 0) return false; + List mulcharmyCheckIdList = new List { _CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos }; + if (mulcharmyCheckIdList.Intersect(botSolvedCardIdList).Any()) + { + int enemyFieldCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); + if (enemyFieldCount + 6 < Bot.Hand.Count()) return false; + } + if (botSolvedCardIdList.Contains(_CardId.MaxxC)) + { + if (!activatedCardIdList.Contains(_CardId.AshBlossom) || !activatedCardIdList.Contains(_CardId.EffectVeiler)) return false; + } + return true; + } + + public bool EffectVeilerActivate() + { + if (CheckWhetherNegated(true)) return false; + // negate monster + List shouldNegateList = GetMonsterListForTargetNegate(true, CardType.Monster); + if (shouldNegateList.Count > 0) + { + ClientCard negateTarget = shouldNegateList[0]; + currentNegateCardList.Add(negateTarget); + AI.SelectCard(negateTarget); + return true; + } + + return false; + } + + public bool SeventhTachyonActivate() + { + if (CheckWhetherNegated(true, true, CardType.Spell)) return false; + bool flag = !spSummonedCardIdList.Contains(CardId.ExRyzeal) && !Bot.HasInHand(CardId.ExRyzeal); + flag &= !(!activatedCardIdList.Contains(CardId.IceRyzeal) && summonCount > 0 && Bot.HasInHand(CardId.IceRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.IceRyzeal)); + if (flag) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public bool TripleTacticsTalentActivate() + { + if (CheckWhetherNegated(true)) return false; + if (TripleTacticsTalentDecision(null) == -1) return false; + SelectSTPlace(Card, true); + return true; + } + + public int TripleTacticsTalentDecision(IList options) + { + // gain control? + bool dangerFlag = Enemy.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled() && (c.IsFloodgate() + || c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir) && c.HasPosition(CardPosition.FaceUpDefence))); + if (dangerFlag) + { + if (options == null) return 1; + int res = options.IndexOf(Util.GetStringId(CardId.TripleTacticsTalent, 1)); + if (res >= 0) return res; + } + // draw? + if (!lockBirdSolved) + { + bool checkFlag = CheckCanContinueSummon(); + if (!checkFlag) + { + if (options == null) return 1; + int res = options.IndexOf(Util.GetStringId(CardId.TripleTacticsTalent, 0)); + if (res >= 0) return res; + } + } + // shuffle hand + if (Enemy.Hand.Count() > 0) + { + if (options == null) return 1; + int res = options.IndexOf(Util.GetStringId(CardId.TripleTacticsTalent, 2)); + if (res >= 0) return res; + } + return -1; + } + + public bool PotOfDesiresActivate() + { + if (CheckWhetherNegated(true)) return false; + if (Bot.Deck.Count >= 15) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public bool PotOfDesireActivateForContinue() + { + if (CheckWhetherNegated(true)) return false; + if (Bot.Deck.Count >= 15 && !CheckCanContinueSummon() && CheckRemainInDeck(CardId.IceRyzeal, CardId.SwordRyzeal, CardId.ExRyzeal) > 0) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public bool BonfireActivateToSearchNecessary() + { + if (CheckWhetherNegated(true, true, CardType.Spell)) return false; + bool flag = !spSummonedCardIdList.Contains(CardId.ExRyzeal) && !Bot.HasInHand(CardId.ExRyzeal); + flag |= !activatedCardIdList.Contains(CardId.IceRyzeal) && summonCount > 0 && !Bot.HasInHand(CardId.IceRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.IceRyzeal); + if (flag) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + + public bool BonfireActivate() + { + if (CheckWhetherNegated(true)) return false; + // activate before no60 + if (!activatedCardIdList.Contains(CardId.Number60DugaresTheTimeless)) + { + ClientCard no60 = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.Number60DugaresTheTimeless) && !c.IsDisabled()); + if (no60 != null && no60.Overlays.Count() >= 2) + { + SelectSTPlace(Card, true); + return true; + } + } + + return false; + } + + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + ClientCard lastChainCard = Util.GetLastChainCard(); + if (Duel.LastChainPlayer == 1) + { + // negate + if (lastChainCard != null && lastChainCard.IsMonster()) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + + // not to negate same card in hand + List mulcharmyIdList = new List { _CardId.MulcharmyPurulia, _CardId.MulcharmyFuwalos, _CardId.MulcharmyNyalus }; + if (Duel.Player == 0 && Bot.HasInHand(code) && !mulcharmyIdList.Contains(code)) return false; + + ClientCard graveTarget = Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.GetOriginCode() == code); + if (graveTarget != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(graveTarget); + currentDestroyCardList.Add(graveTarget); + currentNegateCardList.AddRange(Enemy.MonsterZone.Where(c => c != null && c.IsFaceup() && c.IsCode(code))); + return true; + } + } + + // banish target + foreach (ClientCard graveCard in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(graveCard) && graveCard.IsMonster()) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + int code = graveCard.Id; + AI.SelectCard(graveCard); + currentDestroyCardList.Add(graveCard); + return true; + } + } + + // become targets + if (Duel.ChainTargets.Contains(Card)) + { + List enemyMonsters = Enemy.Graveyard.GetMatchingCards(card => card.IsMonster()).ToList(); + if (enemyMonsters.Count > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + int code = enemyMonsters[0].Id; + AI.SelectCard(code); + currentDestroyCardList.Add(enemyMonsters[0]); + return true; + } + } + } + + // avoid danger monster in grave + if (Duel.LastChainPlayer == 1) return false; + List targets = GetDangerousCardinEnemyGrave(true); + if (targets.Count > 0) + { + int code = targets[0].Id; + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + currentDestroyCardList.Add(targets[0]); + return true; + } + + return false; + } + + public bool RyzealPlugInActivate() + { + if (CheckWhetherNegated(true)) return false; + + // spsummon lv4 + if (Duel.Player == 0 && CurrentTiming == -1) + { + bool summonFlag = GetLevel4CountOnField() == 1; + if (GetLevel4CountOnField() == 0) + { + summonFlag |= GetLevel4FinalCountOnField(true, out _) >= 2 && !CheckWhetherHaveFinalMonster(); + } + + // summon extra level4 monster to xyz summon + if (summonFlag) + { + List checkIdList = new List { CardId.NodeRyzeal, CardId.ExRyzeal, CardId.SwordRyzeal, CardId.IceRyzeal }; + foreach (int id in checkIdList) + { + if (activatedCardIdList.Contains(id)) continue; + ClientCard target = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + if (target == null) + { + target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + } + if (target != null) + { + AI.SelectCard(target); + SelectSTPlace(Card, true); + return true; + } + } + + foreach (int id in checkIdList) + { + ClientCard target = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + if (target == null) + { + target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + } + if (target != null) + { + AI.SelectCard(target); + SelectSTPlace(Card, true); + return true; + } + } + } + } + + // spsummon Detonator + if (!Bot.HasInMonstersZone(CardId.RyzealDetonator, true, true, true) && !Duel.CurrentChain.Any(c => c.IsCode(CardId.TwinsOfTheEclipse)) + && !DefaultCheckWhetherCardIdIsNegated(CardId.RyzealDetonator) && !Util.ChainContainPlayer(0)) + { + ClientCard Detonator = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.RyzealDetonator) && c.IsCanRevive()); + if (Detonator == null) + { + Detonator = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.RyzealDetonator) && c.IsCanRevive()); + } + if (Detonator != null) + { + AI.SelectCard(Detonator); + SelectSTPlace(Card, true); + return true; + } + } + + // chain to negate monster effect + if (Bot.HasInSpellZone(CardId.RyzealCross, true, true) && !activatedCardIdList.Contains(CardId.RyzealCross + 2) + && CheckRemainInDeck(CardId.ExRyzeal, CardId.IceRyzeal, CardId.NodeRyzeal, CardId.SwordRyzeal) > 0) + { + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && lastChainCard.IsMonster() && lastChainCard.Controller == 1 && CheckCardShouldNegate(lastChainCard)) + { + bool activateFlag = false; + bool shouldRebornXyz = false; + bool duodriverActivating = Duel.CurrentChain.Any(c => c.IsCode(CardId.RyzealDuodrive) && c.Controller == 0) && activatedCardIdList.Contains(CardId.RyzealDuodrive + 1); + if (duodriverActivating) + { + // check whether have 3+ material + activateFlag = Bot.MonsterZone.Where(c => c != null && c.HasType(CardType.Xyz)).Sum(c => c.Overlays.Count()) >= 3; + } else + { + if (!Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && c.Overlays.Count() > 0)) + { + activateFlag |= Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasType(CardType.Xyz) && c.HasSetcode(SetcodeRyzeal)); + if (!activateFlag) + { + bool hasXyzTarget = Bot.Banished.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && c.IsCanRevive() && c.HasType(CardType.Xyz)); + hasXyzTarget |= Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && c.IsCanRevive() && c.HasType(CardType.Xyz)); + if (hasXyzTarget) + { + activateFlag = true; + shouldRebornXyz = true; + } + } + } + } + + if (activateFlag) + { + ClientCard Detonator = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDetonator)); + if (Detonator == null) + { + Detonator = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDetonator)); + } + if (Detonator != null) + { + AI.SelectCard(Detonator); + SelectSTPlace(Card, true); + return true; + } + + if (shouldRebornXyz) + { + ClientCard duoDriver = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDuodrive)); + if (duoDriver == null) + { + duoDriver = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDuodrive)); + } + if (duoDriver != null) + { + AI.SelectCard(duoDriver); + SelectSTPlace(Card, true); + return true; + } + } else + { + List checkIdList = new List { CardId.NodeRyzeal, CardId.ExRyzeal, CardId.SwordRyzeal, CardId.IceRyzeal }; + foreach (int id in checkIdList) + { + if (activatedCardIdList.Contains(id)) continue; + ClientCard target = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + if (target == null) + { + target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + } + if (target != null) + { + AI.SelectCard(target); + SelectSTPlace(Card, true); + return true; + } + } + foreach (int id in checkIdList) + { + ClientCard target = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + if (target == null) + { + target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + } + if (target != null) + { + AI.SelectCard(target); + SelectSTPlace(Card, true); + return true; + } + } + } + } + } + } + + bool becomeTargetFlag = DefaultOnBecomeTarget() && Card.Location == CardLocation.SpellZone; + bool endPhaseFlag = Duel.Player == 1 && Duel.Phase == DuelPhase.End; + if (becomeTargetFlag || endPhaseFlag) + { + if (!(Duel.CurrentChain.Any(c => c != null && c.Controller == 1 && c.IsCode(_CardId.EvenlyMatched)) && DetonatorDestroySelf != null)) + { + ClientCard Detonator = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDetonator)); + if (Detonator == null) + { + Detonator = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDetonator)); + } + if (Detonator != null) + { + AI.SelectCard(Detonator); + SelectSTPlace(Card, true); + return true; + } + + ClientCard duoDriver = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDuodrive)); + if (duoDriver == null) + { + duoDriver = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCanRevive() && c.IsCode(CardId.RyzealDuodrive)); + } + if (duoDriver != null) + { + AI.SelectCard(duoDriver); + SelectSTPlace(Card, true); + return true; + } + + List checkIdList = new List { CardId.NodeRyzeal, CardId.ExRyzeal, CardId.SwordRyzeal, CardId.IceRyzeal }; + foreach (int id in checkIdList) + { + if (activatedCardIdList.Contains(id)) continue; + ClientCard target = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + if (target == null) + { + target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + } + if (target != null) + { + AI.SelectCard(target); + SelectSTPlace(Card, true); + return true; + } + } + + if (!endPhaseFlag) + { + foreach (int id in checkIdList) + { + ClientCard target = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + if (target == null) + { + target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(id)); + } + if (target != null) + { + AI.SelectCard(target); + SelectSTPlace(Card, true); + return true; + } + } + } + } + } + + return false; + } + + public bool RyzealPlugInActivateFirst() + { + if (CheckWhetherNegated(true)) return false; + if (Duel.Player == 0 && CurrentTiming == -1 && !activatedCardIdList.Contains(CardId.NodeRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.NodeRyzeal)) + { + List nodeCostList = GetCostFromHandAndField(Card, false); + if (nodeCostList.Count() > 0) + { + ClientCard target = Bot.Banished.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.NodeRyzeal)); + if (target == null) + { + target = Bot.Graveyard.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.NodeRyzeal)); + } + if (target != null) + { + AI.SelectCard(target); + SelectSTPlace(Card, true); + return true; + } + } + } + + return false; + } + + public bool RyzealCrossActivateCard() + { + // Activate Cross at first legal opportunity from hand or when set face-down. + if (Card.Location == CardLocation.Hand || (Card.Location == CardLocation.SpellZone && Card.IsFacedown())) + { + return true; + } + + // whether to negate by cross + if (ActivateDescription == Util.GetStringId(CardId.RyzealCross, 1)) + { + ChainInfo currentChainInfo = Duel.GetCurrentSolvingChainInfo(); + if (currentChainInfo != null && !Duel.IsCurrentSolvingChainNegated()) + { + if (CheckCardShouldNegate(currentChainInfo)) + { + Logger.DebugWriteLine("** cross negate"); + activatedCardIdList.Add(CardId.RyzealCross + 2); + botSolvingCross = true; + return true; + } + } + return false; + } + + if (CheckWhetherNegated(true)) return false; + if (Card.Location == CardLocation.SpellZone && Card.IsFaceup()) return false; + bool flag = RyzealCrossActivateRecycleFirst(); + bool canSetMaterial = Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn) && CheckRemainInDeck(CardId.IceRyzeal, CardId.ExRyzeal, CardId.NodeRyzeal, CardId.SwordRyzeal) > 0 + && (Bot.Graveyard.Any(c => c != null && c.HasSetcode(SetcodeRyzeal) && (c.IsCanRevive() || !c.HasType(CardType.Xyz))) || + Bot.Banished.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && (c.IsCanRevive() || !c.HasType(CardType.Xyz)))); + flag |= Bot.MonsterZone.Count(c => c != null && c.IsFaceup() && c.HasType(CardType.Xyz) && c.HasSetcode(SetcodeRyzeal) && (c.Overlays.Count() > 0 || canSetMaterial)) > 0; + + if (Duel.MainPhase.SpecialSummonableCards.Any(c => c.IsCode(CardId.RyzealDuodrive))) + { + flag |= RyzealDuodriveSpSummonCheck(); + } + + return flag; + } + + public bool RyzealCrossActivateRecycleFirst() + { + if (CheckWhetherNegated(true) || !(Card.Location == CardLocation.SpellZone && Card.IsFaceup())) return false; + if (ActivateDescription == Util.GetStringId(CardId.RyzealCross, 1)) + { + return false; + } + List checkIdList = new List { CardId.RyzealPlugIn, CardId.RyzealDuodrive, CardId.RyzealDetonator, CardId.NodeRyzeal, CardId.ExRyzeal, CardId.IceRyzeal, CardId.SwordRyzeal }; + List targetList = new List(); + foreach (int id in checkIdList) + { + ClientCard target = Bot.Graveyard.FirstOrDefault(c => c.IsCode(id)); + if (target != null && (CheckRemainInDeck(id) + Bot.ExtraDeck.Count(c => c.IsCode(id)) + Bot.Hand.Count(c => c.IsCode(id))) == 0) + { + if (target.HasType(CardType.Xyz) && GetLevel4CountOnField() == 1) continue; + targetList.Add(target); + } + + if (targetList.Count() >= 2) + { + AI.SelectCard(targetList); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + + return false; + } + + public bool RyzealCrossActivateRecycleLater() + { + if (CheckWhetherNegated(true) || !(Card.Location == CardLocation.SpellZone && Card.IsFaceup())) return false; + if (ActivateDescription == Util.GetStringId(CardId.RyzealCross, 1)) + { + return false; + } + SortedDictionary> countDict = new SortedDictionary>(); + List checkIdList = new List { CardId.RyzealPlugIn, CardId.RyzealDuodrive, CardId.RyzealDetonator, CardId.NodeRyzeal, CardId.ExRyzeal, CardId.IceRyzeal, CardId.SwordRyzeal }; + foreach (int id in checkIdList) + { + int remainCount = CheckRemainInDeck(id) + Bot.ExtraDeck.Count(c => c.IsCode(id)); + if (!countDict.ContainsKey(remainCount)) + { + countDict.Add(remainCount, new List()); + } + countDict[remainCount].Add(id); + } + + List targetList = new List(); + foreach (KeyValuePair> pair in countDict) + { + foreach (int id in pair.Value) + { + ClientCard target = Bot.Graveyard.FirstOrDefault(c => c.IsCode(id)); + if (target != null) targetList.Add(target); + } + } + + if (targetList.Count() >= 2) + { + AI.SelectCard(targetList); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + + return false; + } + + public bool CrossoutDesignatorActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + // negate + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard() != null) + { + int code = Util.GetLastChainCard().Id; + int alias = Util.GetLastChainCard().Alias; + if (alias != 0 && alias - code < 10) code = alias; + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (CheckRemainInDeck(code) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectAnnounceID(code); + currentNegateCardList.AddRange(Enemy.MonsterZone.Where(c => c != null && c.IsFaceup() && c.IsCode(code))); + return true; + } + } + return false; + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + + ClientCard LastChainCard = Util.GetLastChainCard(); + + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ((this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || Util.IsChainTarget(Card) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + if (target != null) + { + AI.SelectCard(target); + } + else + { + AI.SelectCard(Enemy.GetMonsters()); + } + infiniteImpermanenceList.Add(this_seq); + return true; + } + } + + // negate monster + List shouldNegateList = GetMonsterListForTargetNegate(true, CardType.Trap); + if (shouldNegateList.Count > 0) + { + ClientCard negateTarget = shouldNegateList[0]; + currentNegateCardList.Add(negateTarget); + + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(negateTarget); + return true; + } + + return false; + } + + // TODO extra summon + + public bool AbyssDwellerSummonCheck() + { + bool flag = enemyDeckTypeRecord.Contains(SetcodeAtlantean); + flag |= enemyDeckTypeRecord.Contains(SetcodeOrcust); + flag |= enemyDeckTypeRecord.Contains(SetcodePhantomKnight); + flag |= enemyDeckTypeRecord.Count() > 0 && enemyDeckTypeRecord.All(c => c == SetcodeTearlaments); + + int enemyDeckTotalCount = Enemy.Hand.Count() + Enemy.Deck.Count() + Enemy.Graveyard.Count() + Enemy.Banished.Count() + Enemy.ExtraDeck.Count(); + if (enemyDeckTotalCount > 65) + { + flag |= !enemyDeckTypeRecord.Contains(SetcodeInfernoid); + } + + return flag; + } + + public bool Number41BagooskatheTerriblyTiredTapirSummonCheck() + { + bool flag = enemyDeckTypeRecord.Contains(SetcodeFloowandereeze); + flag |= enemyDeckTypeRecord.Contains(SetcodeBranded); + flag |= enemyDeckTypeRecord.Contains(SetcodeRyzeal); + flag &= Util.IsTurn1OrMain2(); + + return flag; + } + + public bool TornadoDragonSummonCheck() + { + if (CheckWhetherNegated(true, true, CardType.Monster)) return false; + bool flag = enemyDeckTypeRecord.Contains(SetcodeLabrynth); + flag |= Enemy.SpellZone.Any(c => c != null && c.IsFaceup() && !c.IsDisabled() && c.IsFloodgate()); + flag |= Enemy.SpellZone.Count(c => c != null && !c.IsShouldNotBeMonsterTarget() && !NotToDestroySpellTrap.Contains(c.Id)) >= 3; + flag |= !Util.IsTurn1OrMain2() && !botSolvedCardIdList.Contains(_CardId.EvilswarmExcitonKnight) && Enemy.GetMonsterCount() == 0 + && Enemy.SpellZone.Count(c => c != null && !c.IsShouldNotBeMonsterTarget() && !NotToDestroySpellTrap.Contains(c.Id)) > 0; + + return flag; + } + + public bool EvilswarmExcitonKnightSpSummon() + { + if (CheckWhetherNegated(true, true, CardType.Monster) || Duel.Turn == 1) return false; + + return DefaultEvilswarmExcitonKnightSummon(); + } + + public bool LessSpSummonExtra() + { + if (!CheckShouldNoMoreSpSummon(CardLocation.Extra)) return false; + + // No.41 + ClientCard no41 = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir)); + if (no41 != null) + { + if (Number41BagooskatheTerriblyTiredTapirSummonCheck()) + { + if (Card != no41) return false; + + // Select 2 monster with lowest defense + List materialList = GetLevel4OnField(null); + if (materialList.Count() >= 2) + { + AI.SelectMaterials(materialList); + return true; + } + } + } + // abyss + ClientCard abyss = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(CardId.AbyssDweller)); + if (abyss != null) + { + if (AbyssDwellerSummonCheck()) + { + if (Card != abyss) return false; + + // Select 2 monster with lowest defense + List materialList = GetLevel4OnField(null); + if (materialList.Count() >= 2) + { + AI.SelectMaterials(materialList); + return true; + } + } + } + // Detonator + ClientCard Detonator = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(CardId.RyzealDetonator)); + if (Detonator != null) + { + List materialList = GetLevel4OnField(c => c.HasSetcode(SetcodeRyzeal)); + if (materialList.Count() >= 2) + { + if (Card != Detonator) return false; + AI.SelectMaterials(materialList); + return true; + } + } + + return false; + } + + public bool FirstRyzealDuodriveSpSummon() + { + if (!RyzealDuodriveSpSummonCheck()) return false; + if (Bot.Graveyard.Count(c => c.HasSetcode(SetcodeRyzeal) && c.IsMonster()) == 0) + { + if (!CheckShouldNoMoreSpSummon(CardLocation.Hand) && Bot.HasInHand(CardId.ExRyzeal) + && !spSummonedCardIdList.Contains(CardId.ExRyzeal) && Duel.MainPhase.SpecialSummonableCards.Any(c => c.IsCode(CardId.ExRyzeal)) + && Bot.ExtraDeck.Count(c => c.IsCode(CardId.RyzealDetonator, CardId.RyzealDuodrive)) > 2) + { + return false; + } + } + + List materialList = GetLevel4OnField(null); + List materialExceptNode = materialList + .Where(c => !(c.IsCode(CardId.NodeRyzeal) && !c.IsDisabled() && !activatedCardIdList.Contains(CardId.NodeRyzeal))).ToList(); + + if (materialExceptNode.Count() >= 2) + { + AI.SelectMaterials(materialExceptNode.Take(2).ToList()); + return true; + } + if (materialList.Count() > 2 && !CheckCanContinueSummon()) + { + AI.SelectMaterials(materialList.Take(2).ToList()); + return true; + } + if (materialList.Count() >= 2 && !CheckCanContinueSummon(true)) + { + AI.SelectMaterials(materialList.Take(2).ToList()); + return true; + } + + return false; + } + + public bool RyzealDuodriveSpSummonCheck() + { + bool checkFlag = Duel.MainPhase.SpecialSummonableCards.Any(c => c.IsCode(CardId.RyzealDuodrive)); + checkFlag &= !Bot.HasInMonstersZone(CardId.RyzealDuodrive, true, true, true); + checkFlag &= CheckRemainInDeck(CardId.IceRyzeal, CardId.SwordRyzeal, CardId.NodeRyzeal, CardId.ExRyzeal, CardId.RyzealPlugIn, CardId.RyzealCross) >= 2; + checkFlag &= !DefaultCheckWhetherCardIdIsNegated(CardId.RyzealDuodrive); + checkFlag &= !activatedCardIdList.Contains(CardId.RyzealDuodrive + 1); + checkFlag &= !CheckWhetherNegated(true, true, CardType.Monster); + checkFlag &= !lockBirdSolved; + checkFlag &= !CheckShouldNoMoreSpSummon(CardLocation.Extra); + + return checkFlag; + } + + public bool SecondXyzSummon() + { + if (Card.Location != CardLocation.Extra) return false; + + int level4Count = GetLevel4FinalCountOnField(true, out _); + bool result = SecondXyzSummonInner(); + return result; + } + + public bool SecondXyzSummonInner() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Extra)) return false; + + // summon after duo driver + if (RyzealDuodriveSpSummonCheck()) { + return false; + } + + // check whether have 4 monsters for material. + // if not, skip the second xyz monster. + int level4Count = GetLevel4FinalCountOnField(true, out bool hasNode); + if (level4Count <= 4) return false; + + // select which monster to summon + List materialList = GetLevel4OnField(null); + List materialExceptNode = materialList + .Where(c => !(c.IsCode(CardId.NodeRyzeal) && !c.IsDisabled() && !activatedCardIdList.Contains(CardId.NodeRyzeal))).ToList(); + + // abyss + ClientCard abyss = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(CardId.AbyssDweller)); + if (abyss != null) + { + if (AbyssDwellerSummonCheck()) + { + if (Card != abyss) return false; + + if (materialExceptNode.Count() >= 2) + { + AI.SelectMaterials(materialExceptNode.Take(2).ToList()); + return true; + } + if (materialList.Count() > 2 && !CheckCanContinueSummon()) + { + AI.SelectMaterials(materialList.Take(2).ToList()); + return true; + } + } + } + // 41 + ClientCard no41 = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir)); + if (no41 != null) + { + bool flag = hasNode; + flag &= Util.IsTurn1OrMain2(); + flag &= Bot.HasInExtra(CardId.TwinsOfTheEclipse) && Bot.MonsterZone.Any(c => c != null && c.IsFaceup() && c.HasType(CardType.Xyz)); + flag &= (GetNegateEffectCount() >= 2 || lockBirdSolved); + + if (flag) + { + if (Card != no41) return false; + + if (materialExceptNode.Count() >= 2) + { + AI.SelectMaterials(materialExceptNode.Take(2).ToList()); + return true; + } + if (materialList.Count() >= 2 && Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn)) + { + AI.SelectMaterials(materialList.Take(2).ToList()); + return true; + } + } + } + + // photon blast dragon + ClientCard photonDragon = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(CardId.StarliegePhotonBlastDragon)); + bool have2MaterialDuo = false; + if (photonDragon != null) + { + int duoDriveOverlayCount = 0; + foreach (ClientCard monster in Bot.MonsterZone) + { + if (monster == null || !monster.IsCode(CardId.RyzealDuodrive)) continue; + duoDriveOverlayCount += monster.Overlays.Count(); + } + + if (Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn)) duoDriveOverlayCount++; + have2MaterialDuo = duoDriveOverlayCount >= 2; + } + + if (photonDragon != null && have2MaterialDuo && enemyDeckTypeRecord.Contains(SetcodeSkyStriker)) + { + if (Card != photonDragon) return false; + + if (materialExceptNode.Count() >= 2) + { + AI.SelectMaterials(materialExceptNode.Take(2).ToList()); + return true; + } + } + + // 60 + ClientCard no60 = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(CardId.Number60DugaresTheTimeless)); + if (no60 != null && !lockBirdSolved) + { + bool flag = Bot.Deck.Count() > 2; + + if (flag) + { + if (Card != no60) return false; + + if (materialExceptNode.Count() >= 2) + { + AI.SelectMaterials(materialExceptNode.Take(2).ToList()); + return true; + } + if (materialList.Count() >= 2 && Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn)) + { + AI.SelectMaterials(materialList.Take(2).ToList()); + return true; + } + } + } + + if (photonDragon != null && have2MaterialDuo) + { + if (Card != photonDragon) return false; + + if (materialExceptNode.Count() >= 2) + { + AI.SelectMaterials(materialExceptNode.Take(2).ToList()); + return true; + } + } + + // tornado dragon + ClientCard Detonator = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(CardId.RyzealDetonator)); + if (Detonator == null) + { + ClientCard tornadoDragon = Duel.MainPhase.SummonableCards.FirstOrDefault(c => c.IsCode(CardId.TornadoDragon)); + if (tornadoDragon != null && TornadoDragonSummonCheck() && Card == tornadoDragon) + { + if (materialExceptNode.Count() >= 2) + { + AI.SelectMaterials(materialExceptNode.Take(2).ToList()); + return true; + } + if (materialList.Count() >= 2 && Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn)) + { + AI.SelectMaterials(materialList.Take(2).ToList()); + return true; + } + } + } + + // Detonator + if (Detonator != null && Card == Detonator) + { + if (materialExceptNode.Count() >= 2) + { + AI.SelectMaterials(materialExceptNode.Take(2).ToList()); + return true; + } + if (materialList.Count() >= 2 && Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn)) + { + AI.SelectMaterials(materialList.Take(2).ToList()); + return true; + } + } + + // TODO + + + return false; + } + + public bool TwinsOfTheEclipseSpSummon() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Extra)) return false; + if (Util.IsTurn1OrMain2()) + { + bool hasNode = Bot.HasInHand(CardId.NodeRyzeal) && !spSummonedCardIdList.Contains(CardId.NodeRyzeal); + hasNode |= Bot.HasInMonstersZone(CardId.NodeRyzeal, true, false, true); + if (Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn)) + { + hasNode |= Bot.Graveyard.Any(c => c.IsCode(CardId.NodeRyzeal)); + hasNode |= Bot.Banished.Any(c => c.IsFaceup() && c.IsCode(CardId.NodeRyzeal)); + } + hasNode &= !activatedCardIdList.Contains(CardId.NodeRyzeal) && !DefaultCheckWhetherCardIdIsNegated(CardId.NodeRyzeal); + + List materialList = new List(); + ClientCard duoDriver = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.RyzealDuodrive)); + if (activatedCardIdList.Contains(CardId.RyzealDuodrive + 1) && duoDriver != null) + { + materialList.Add(duoDriver); + hasNode |= !CheckWhetherWillbeRemoved() && duoDriver.Overlays.Any(id => id == CardId.NodeRyzeal); + } + ClientCard no60 = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(CardId.Number60DugaresTheTimeless)); + if (no60 != null && (activatedCardIdList.Contains(CardId.Number60DugaresTheTimeless) || no60.IsDisabled())) + { + materialList.Add(no60); + hasNode |= !CheckWhetherWillbeRemoved() && no60.Overlays.Any(id => id == CardId.NodeRyzeal); + } + ClientCard no41 = Bot.MonsterZone.FirstOrDefault(c => c != null && c.IsFaceup() && c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir)); + if (no41 != null) + { + materialList.Add(no41); + hasNode |= !CheckWhetherWillbeRemoved() && no41.Overlays.Any(id => id == CardId.NodeRyzeal); + } + + if (materialList.Count() >= 2 && hasNode) + { + AI.SelectMaterials(materialList); + return true; + } + } + else + { + if (botSolvedCardIdList.Contains(_CardId.EvilswarmExcitonKnight)) + { + return false; + } + + List materialList = GetLevel4OnField(null); + List xyzMonsterList = Bot.MonsterZone.Where(c => c != null && c.IsFaceup() && c.HasType(CardType.Xyz) && c.Rank == 4 && c.Attack < 2500).ToList(); + int level4Count = GetLevel4FinalCountOnField(true, out bool hasNode) + xyzMonsterList.Count(); + + if (level4Count < 4) return false; + + materialList.AddRange(xyzMonsterList); + materialList = materialList.Where(c => c != null && c.Attack < 2500).OrderBy(c => c.GetDefensePower()).Take(2).ToList(); + if (materialList.Count() >= 2) + { + // check whether enemy have monster with 2more less than 2500 + bool checkFlag = Enemy.MonsterZone.Count(c => c != null && c.GetDefensePower() < 2500) >= 2 && !CheckWhetherNegated(true, true, CardType.Monster) + && !DefaultCheckWhetherCardIdIsNegated(CardId.TwinsOfTheEclipse); + if (checkFlag && materialList.Sum(c => c.Attack) < 5000) + { + AI.SelectMaterials(materialList); + return true; + } + else if (materialList.Sum(c => botSolvedCardIdList.Contains(CardId.RyzealPlugIn) && !c.HasType(CardType.Xyz) ? 0 : c.Attack) < 2500 + && !Duel.MainPhase.SpecialSummonableCards.Any(c => c.IsCode(CardId.RyzealDetonator))) + { + AI.SelectMaterials(materialList); + return true; + } + } + } + + return false; + } + + public bool FinalXyzSummon() + { + if (Card.Location != CardLocation.Extra) return false; + + int level4Count = GetLevel4FinalCountOnField(false, out _); + bool result = FinalXyzSummonInner(); + + return result; + } + + public bool FinalXyzSummonInner() + { + // summon after duo driver + if (RyzealDuodriveSpSummonCheck()) + { + return false; + } + + int level4Count = GetLevel4FinalCountOnField(false, out _); + if (level4Count < 2) return false; + + // No.41 + ClientCard no41 = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir)); + if (no41 != null) + { + if (Number41BagooskatheTerriblyTiredTapirSummonCheck()) + { + if (Card != no41) return false; + + // Select 2 monster with lowest defense + List materialList = GetLevel4OnField(null); + if (materialList.Count() >= 2) + { + AI.SelectMaterials(materialList); + return true; + } + } + } + + // abyss + ClientCard abyss = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(CardId.AbyssDweller)); + if (abyss != null) + { + if (AbyssDwellerSummonCheck()) + { + if (Card != abyss) return false; + + // Select 2 monster with lowest defense + List materialList = GetLevel4OnField(null); + if (materialList.Count() >= 2) + { + AI.SelectMaterials(materialList); + return true; + } + } + } + // Detonator + ClientCard Detonator = Duel.MainPhase.SpecialSummonableCards.FirstOrDefault(c => c.IsCode(CardId.RyzealDetonator)); + if (Detonator != null) + { + List materialList = GetLevel4OnField(c => c.HasSetcode(SetcodeRyzeal)); + if (materialList.Count() >= 2) + { + if (Card != Detonator) return false; + AI.SelectMaterials(materialList); + return true; + } + } + + // tornado dragon + if (Detonator == null) + { + ClientCard tornadoDragon = Duel.MainPhase.SummonableCards.FirstOrDefault(c => c.IsCode(CardId.TornadoDragon)); + if (tornadoDragon != null && TornadoDragonSummonCheck()) + { + // Select 2 monster with lowest defense + List materialList = GetLevel4OnField(null); + if (materialList.Count() >= 2) + { + AI.SelectMaterials(materialList); + return true; + } + } + } + + return false; + } + + public bool DonnerDaggerFurHireSpSummon() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Extra)) + { + return false; + } + bool haveEnemyTarget = Enemy.MonsterZone.Any(c => c != null && !c.IsShouldNotBeMonsterTarget()) && !CheckWhetherNegated(true, true, CardType.Monster); + + List illegalList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.Level != 4 && card.Rank != 4).OrderBy(c => c.GetDefensePower()).ToList(); + bool necessary = Bot.HasInHand(CardId.ExRyzeal) && !spSummonedCardIdList.Contains(CardId.ExRyzeal) && !activatedCardIdList.Contains(CardId.ExRyzeal) + && illegalList.Count() > 0; + bool needDestory = !CheckWhetherNegated(true, true, CardType.Monster) + && Enemy.MonsterZone.Any(c => c != null && !c.IsShouldNotBeMonsterTarget() && c.IsFloodgate() && !c.IsDisabled()); + necessary |= needDestory; + + if (necessary) + { + if (illegalList.Count() == 1 && haveEnemyTarget) + { + List otherMaterialList = Bot.GetMonsters().Where(card => card.IsFaceup() && !illegalList.Contains(card) && (card.Owner == 1 || !card.HasType(CardType.Xyz))).ToList(); + otherMaterialList.Sort(CardContainer.CompareCardAttack); + illegalList.AddRange(otherMaterialList); + } + + if (illegalList.Count() > 1) + { + List materialList = illegalList.Take(2).ToList(); + if (Util.GetBotAvailZonesFromExtraDeck(materialList) > 0) + { + AI.SelectMaterials(materialList); + return true; + } + } + } + + if (Duel.Phase == DuelPhase.Main2) + { + List enemyOwnerMonsters = Bot.MonsterZone.Where(c => c != null && c.IsFaceup() && c.Owner == 1).OrderBy(c => c.GetDefensePower()).ToList(); + if (enemyOwnerMonsters.Count() > 0 && haveEnemyTarget) + { + if (enemyOwnerMonsters.Count() == 1) + { + List otherMaterialList = Bot.GetMonsters() + .Where(card => card.IsFaceup() && !enemyOwnerMonsters.Contains(card) && (!card.HasType(CardType.Xyz) || card.Overlays.Count() == 0)) + .OrderBy(c => c.GetDefensePower()).ToList(); + enemyOwnerMonsters.AddRange(otherMaterialList); + } + + if (enemyOwnerMonsters.Count() > 1) + { + List materialList = enemyOwnerMonsters.Take(2).ToList(); + if (Util.GetBotAvailZonesFromExtraDeck(materialList) > 0) + { + AI.SelectMaterials(materialList); + return true; + } + } + } + } + + return false; + } + + // TODO extra effect + + + public bool MereologicAggregatorActivateFirst() + { + List> multiNegateFuncList = new List> { + {c => c.IsCode(44665365, 48546368, 54178659) && c.IsMonster() }, + {c => c.IsCode(4280258) && c.Attack >= 800 }, + {c => c.IsCode(47297616) && c.Attack >= 500 && c.Defense >= 500 }, + {c => c.IsCode(19652159) && c.Attack >= 1000 && c.Defense >= 1000 }, + {c => c.IsCode(79600447) && Enemy.MonsterZone.Any(m => m != null && m.IsFaceup() && m.IsCode(23288411) && m.Attack >= 1000) } + }; + List searchCardList = new List(Enemy.GetMonsters()); + searchCardList.AddRange(Enemy.GetSpells()); + foreach (ClientCard card in searchCardList) + { + if (card == null || card.IsFacedown() || card.IsDisabled()) continue; + foreach (Func func in multiNegateFuncList) + { + if (func(card)) + { + AI.SelectCard(card); + currentNegateCardList.Add(card); + activatedCardIdList.Add(Card.Id + 2); + return true; + } + } + } + return false; + } + + public bool MereologicAggregatorActivateLater() + { + if (CheckWhetherNegated(true)) return false; + + ClientCard lastChainCard = Util.GetLastChainCard(); + + // for Chain1 ExRyzeal Chain2 Mereo to negate No41 + if (lastChainCard != null && lastChainCard.Controller == 0 && lastChainCard.IsCode(CardId.ExRyzeal)) + { + ClientCard no41 = Enemy.GetMonsters().FirstOrDefault(c => + c.IsFaceup() && !c.IsDisabled() && c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir) && c.HasPosition(CardPosition.FaceUpDefence) && !currentNegateCardList.Contains(c)); + if (no41 != null) + { + currentNegateCardList.Add(no41); + AI.SelectCard(no41); + activatedCardIdList.Add(Card.Id + 2); + return true; + } + } + + List targetList = GetNormalEnemyTargetList(true, false, CardType.Monster, true).Where(c => c.IsFaceup() && !c.IsDisabled()).ToList(); + if (targetList.Count() > 0) + { + currentNegateCardList.Add(targetList[0]); + AI.SelectCard(targetList); + activatedCardIdList.Add(Card.Id + 2); + return true; + } + + // protect chain + if (lastChainCard != null && lastChainCard.Controller == 0 && lastChainCard.IsCode(CardId.ExRyzeal)) + { + foreach (ClientCard card in Bot.GetMonsters()) + { + if (card.IsFacedown() || Duel.CurrentChain.Contains(card) || card.IsDisabled() || !card.HasType(CardType.Effect)) continue; + bool flag = card.IsCode(CardId.IceRyzeal, CardId.SwordRyzeal); + flag |= card.IsCode(CardId.NodeRyzeal) && activatedCardIdList.Contains(CardId.NodeRyzeal); + flag |= card.HasType(CardType.Xyz) && !card.HasXyzMaterial() && !card.IsCode(CardId.RyzealDetonator, CardId.RyzealDuodrive, CardId.FullArmoredUtopicRayLancer); + if (flag) + { + AI.SelectCard(card); + activatedCardIdList.Add(Card.Id + 2); + return true; + } + } + } + + return false; + } + + public bool RyzealDetonatorActivate() + { + if (ActivateDescription == 96) + { + Logger.DebugWriteLine("** Detonator replace destroy"); + if (DetonatorDestroySelf != Card) + { + activatedCardIdList.Add(CardId.RyzealDetonator + 2); + return true; + } + return false; + } + if (CheckWhetherNegated(true)) return false; + if (ActivateDescription == Util.GetStringId(CardId.RyzealDetonator, 0)) + { + // destroy self + bool shouldDestroySelf = false; + bool willBeNegated = false; + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && lastChainCard.Controller == 1 && lastChainCard.IsCode(targetNegateIdList)) + { + shouldDestroySelf = true; + willBeNegated = true; + } + shouldDestroySelf |= Duel.CurrentChain.Any(c => c != null && c.Controller == 1 && !c.IsDisabled() && !DefaultCheckWhetherCardIdIsNegated(c.Id) + && c.IsCode(_CardId.EvenlyMatched, 35480699)); + shouldDestroySelf |= Card.Overlays.Count() == 1 && !activatedCardIdList.Contains(CardId.RyzealDetonator) + && GetProblematicEnemyCardList(true, false, CardType.Monster).Count() == 0; + + if (shouldDestroySelf) + { + bool canRebornSelf = Bot.SpellZone.Count(c => c != null && c.IsFacedown() && c.IsCode(CardId.RyzealPlugIn) && !Duel.ChainTargets.Contains(c)) > 0; + bool canActivateTwin = !activatedCardIdList.Contains(CardId.TwinsOfTheEclipse + 1) && !DefaultCheckWhetherCardIdIsNegated(CardId.TwinsOfTheEclipse) + && !CheckWhetherWillbeRemoved(); + canRebornSelf |= canActivateTwin && Card.Overlays.Contains(CardId.TwinsOfTheEclipse); + if (Duel.CurrentChain.Any(c => c != null && c.Controller == 1 && !c.IsDisabled() && !DefaultCheckWhetherCardIdIsNegated(c.Id) + && c.IsCode(_CardId.EvenlyMatched))) + { + canRebornSelf |= Bot.MonsterZone.Any(c => c != null && c.HasType(CardType.Xyz) && c.Overlays.Contains(CardId.TwinsOfTheEclipse)); + } + + if (canRebornSelf) + { + DetonatorDestroySelf = Card; + return true; + } + } + + + // destroy + if (CanDestroyList(willBeNegated).Count() > 0) + { + return true; + } + } + else + { + // attach + activatedCardIdList.Add(Card.Id); + return true; + } + return false; + + } + + public bool RyzealDuodriveActivate() + { + if (CheckWhetherNegated(true)) return false; + + if (ActivateDescription == Util.GetStringId(CardId.RyzealDuodrive, 1)) + { + int overlayCount = 0; + foreach (ClientCard card in Bot.MonsterZone) + { + if (card == null || card.Overlays.Count() == 0) continue; + if (card.IsCode(CardId.Number60DugaresTheTimeless) && !card.IsDisabled() && !activatedCardIdList.Contains(CardId.Number60DugaresTheTimeless)) + { + continue; + } + overlayCount += card.Overlays.Count(); + } + if (overlayCount >= 2) + { + // deattach and search + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } else + { + // attach + activatedCardIdList.Add(Card.Id); + return true; + } + + return false; + } + + public bool TwinsOfTheEclipseActivate() + { + if (Card.Location == CardLocation.MonsterZone) + { + // double attack + // dump remove material + if (CheckWhetherNegated(true)) return Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn); + activatedCardIdList.Add(Card.Id); + return true; + + } else if (Card.Location == CardLocation.Grave) + { + // spsummon + if (CheckWhetherNegated(true)) return false; + + activatedCardIdList.Add(Card.Id + 1); + ClientCard rebornTarget = TwinsOfTheEclipseRebornTarget(null); + if (rebornTarget != null) + { + ClientCard mereo = Bot.Graveyard.FirstOrDefault(c => c.IsCode(CardId.MereologicAggregator)); + if (mereo != null) + { + AI.SelectCard(new List { rebornTarget, mereo }); + return true; + } + ClientCard nonLightDark = Bot.Graveyard.FirstOrDefault(c => c.HasType(CardType.Xyz) && !c.HasAttribute((CardAttribute)attrbuteLightDark)); + if (nonLightDark != null) + { + AI.SelectCard(new List { rebornTarget, nonLightDark }); + return true; + } + ClientCard xyzMonster = Bot.Graveyard.FirstOrDefault(c => c.HasType(CardType.Xyz)); + if (xyzMonster != null) + { + AI.SelectCard(new List { rebornTarget, xyzMonster }); + return true; + } + } + + // although cannot find target, still should activate. + return true; + } + + return false; + } + + public ClientCard TwinsOfTheEclipseRebornTarget(List targetList) + { + if (targetList == null) + { + targetList = Bot.Graveyard.Where(c => c.HasType(CardType.Xyz) && c.IsCanRevive()).ToList(); + } + ClientCard duoDriver = targetList.FirstOrDefault(c => c.IsCode(CardId.RyzealDuodrive)); + ClientCard Detonator = targetList.FirstOrDefault(c => c.IsCode(CardId.RyzealDetonator)); + ClientCard no41 = targetList.FirstOrDefault(c => c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir)); + ClientCard abyssDweller = targetList.FirstOrDefault(c => c.IsCode(CardId.AbyssDweller)); + + if (no41 != null && !DefaultCheckWhetherCardIdIsNegated(_CardId.Number41BagooskatheTerriblyTiredTapir) + && !(Detonator != null && !activatedCardIdList.Contains(CardId.RyzealDetonator)) + && !(Duel.Turn == 1 && duoDriver != null)) + { + return no41; + } + + if (abyssDweller != null && !DefaultCheckWhetherCardIdIsNegated(CardId.AbyssDweller) && !botSolvedCardIdList.Contains(CardId.AbyssDweller) + && AbyssDwellerSummonCheck()) + { + return abyssDweller; + } + + if (Detonator != null) + { + return Detonator; + } + if (duoDriver != null && (!activatedCardIdList.Contains(CardId.RyzealDuodrive + 1) || Bot.HasInHandOrInSpellZone(CardId.RyzealCross))) + { + return duoDriver; + } + + // random spsummon + if (targetList.Count() > 0) + { + return ShuffleList(targetList)[0]; + } + return null; + } + + public List CanDestroyList(bool ignoreCurrentDestroy = false) + { + List destroyTargetList = GetNormalEnemyTargetList(true, ignoreCurrentDestroy, CardType.Monster).Except(currentNegateCardList).ToList(); + + List cannotDestroyList = new List(NotToDestroySpellTrap); + destroyTargetList.RemoveAll(c => c.IsCode(cannotDestroyList)); + + List undestoryableCardIdlist = new List { 94977269, 58604027, 8062132, 10817524, 53315891, 10000090, 86221741, 71222868, + 83257450, 97489701, 97165977, 24550676, 55410871, 72664875, 85908279, 13331639, 20654247, 43228023, 99585850, 92770064, 10497636, 77313225 }; + destroyTargetList.RemoveAll(c => !c.IsDisabled() && c.IsCode(undestoryableCardIdlist)); + + destroyTargetList.RemoveAll(c => !c.IsDisabled() && c.HasSetcode(SetcodeMajespecter)); + + if (Enemy.GetSpells().Any(c => c.IsFacedown()) || Enemy.GetMonsters().Any(c => c.IsFacedown())) + { + destroyTargetList.RemoveAll(c => c.IsCode(81497285)); + } + destroyTargetList.RemoveAll(c => !c.IsDisabled() && c.HasSetcode(SetcodeMajespecter)); + + + return destroyTargetList; + } + + public bool TornadoDragonActivate() + { + if (CheckWhetherNegated(true)) return false; + + List spells = Enemy.GetSpells(); + if (spells.Count == 0) + return false; + + // destroy faceup card first + ClientCard selected = Enemy.SpellZone.GetFloodgate(); + if (selected == null && Duel.Player == 1) + { + List targetList = spells.Where(c => c.IsFaceup() && !NotToDestroySpellTrap.Contains(c.Id) && !currentDestroyCardList.Contains(c) + && c.HasType(CardType.Continuous | CardType.Equip | CardType.Field | CardType.Pendulum)).ToList(); + if (targetList.Count() > 0) + { + selected = ShuffleList(targetList)[0]; + } + } + + if (selected != null) + { + currentDestroyCardList.Add(selected); + AI.SelectCard(selected); + return true; + } + + // destroy face-down card + if (selected == null) + { + List setThisTurnList = Enemy.SpellZone.Where(c => c != null && c.IsFacedown() && !currentDestroyCardList.Contains(c) + && enemyPlaceThisTurn.Contains(c)).ToList(); + if (setThisTurnList.Count() > 0) + { + selected = ShuffleList(setThisTurnList)[0]; + } + } + if (selected == null) + { + List setThisTurnList = Enemy.SpellZone.Where(c => c != null && c.IsFacedown() && !currentDestroyCardList.Contains(c)).ToList(); + if (setThisTurnList.Count() > 0) + { + selected = ShuffleList(setThisTurnList)[0]; + } + } + + bool flag = Duel.Player == 0; + flag |= Duel.Player == 1 && Duel.Phase == DuelPhase.End; + flag |= DefaultOnBecomeTarget(); + + if (flag) + { + currentDestroyCardList.Add(selected); + AI.SelectCard(selected); + return true; + } + + return false; + } + + public bool EvilswarmExcitonKnightActivate() + { + if (CheckWhetherNegated(true)) return false; + return DefaultEvilswarmExcitonKnightEffect(); + } + + public bool AbyssDwellerActivate() + { + if (botSolvedCardIdList.Contains(CardId.AbyssDweller)) return false; + + if (Duel.Player == 0 && Bot.HasInHandOrInSpellZone(CardId.RyzealPlugIn)) + { + List checkIdList = new List { CardId.NodeRyzeal, CardId.SwordRyzeal, CardId.ExRyzeal }; + foreach (int checkId in checkIdList) + { + if (Card.Overlays.Contains(checkId) && !Bot.HasInHand(checkId) && !activatedCardIdList.Contains(checkId)) + { + return true; + } + return false; + } + } + if (Duel.Player == 1) + { + if (CheckWhetherNegated(true)) return false; + if (enemyDeckTypeRecord.Contains(SetcodeAtlantean)) return true; + return Enemy.GetMonsterCount() + Enemy.GetSpellCount() > 0; + } + + return false; + } + + public bool Number60DugaresTheTimelessActivate() + { + if (CheckWhetherNegated(true)) return false; + if (Number60DugaresTheTimelessDrawEffect() || Number60DugaresTheTimelessDoubleTarget() != null || Number60DugaresTheTimelessRebornEffect()) + { + activatedCardIdList.Add(Card.Id); + return true; + } + return false; + } + + public bool Number60DugaresTheTimelessDrawEffect() + { + if (lockBirdSolved || Bot.Deck.Count < 2) return false; + activatedCardIdList.Add(Card.Id); + return true; + } + + public ClientCard Number60DugaresTheTimelessDoubleTarget() + { + if (Util.IsTurn1OrMain2()) return null; + ClientCard maxAttackMonster = Bot.MonsterZone.Where(c => c != null && (c.HasPosition(CardPosition.FaceUpAttack) || !summonThisTurn.Contains(c))) + .OrderByDescending(c => c.Attack).FirstOrDefault(); + + if (maxAttackMonster != null) + { + int maxBotAttack = maxAttackMonster.Attack; + + // defeat enemy monster + ClientCard bestEnemyMonster = Enemy.MonsterZone.Where(c => c != null && c.IsFaceup() && (c.IsDisabled() || !c.IsMonsterInvincible())) + .OrderByDescending(c => c.GetDefensePower()).FirstOrDefault(); + if (bestEnemyMonster != null) + { + int maxEnemyPower = bestEnemyMonster.GetDefensePower(); + if (bestEnemyMonster.IsAttack()) maxEnemyPower -= 1; + if (maxBotAttack < maxEnemyPower && maxBotAttack * 2 > maxEnemyPower) + { + return maxAttackMonster; + } + } + + // direct attack + if (!botSolvedCardIdList.Contains(_CardId.EvilswarmExcitonKnight)) + { + int currentAttack = GetBotCurrentTotalAttack(); + if (currentAttack < Enemy.LifePoints && currentAttack + maxBotAttack >= Enemy.LifePoints) + { + return maxAttackMonster; + } + } + } + + return null; + } + + public bool Number60DugaresTheTimelessRebornEffect() + { + // not used + + return false; + } + + public bool DonnerDaggerFurHireActivate() + { + if (CheckAtAdvantage() && !Bot.HasInHand(CardId.ExRyzeal)) + { + return false; + } + + ClientCard targetCard = GetProblematicEnemyMonster(canBeTarget: true, selfType: CardType.Monster); + if (targetCard == null) + { + List enemyMonsters = Enemy.GetMonsters(); + if (enemyMonsters.Count() > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + targetCard = enemyMonsters[0]; + } + } + + if (targetCard != null) + { + AI.SelectCard(Card); + AI.SelectNextCard(targetCard); + currentDestroyCardList.Add(targetCard); + return true; + } + + return false; + } + + + + public bool Level4Summon() + { + if (CheckShouldNoMoreSpSummon(CardLocation.Hand | CardLocation.Extra)) return false; + ClientCard leastAttackLevel4 = Bot.Hand.Where(c => c.Level == 4).OrderBy(c => c.Attack).FirstOrDefault(); + if (leastAttackLevel4 == null || Card != leastAttackLevel4) return false; + + if (GetLevel4CountOnField() == 1) + { + ClientCard target = Duel.MainPhase.SummonableCards.Where(c => c != null && c.Level == 4).OrderBy(c => c.Attack).FirstOrDefault(); + if (Card != target) return false; + summonCount -= 1; + return true; + } + + return false; + } + + + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + + // select place + if ((Card.IsTrap() || Card.HasType(CardType.QuickPlay))) + { + // do not set infinite impermanence if don't need to set other cards + if (Card.IsCode(_CardId.InfiniteImpermanence) && Bot.GetMonsterCount() == 0 && Bot.GetSpellCount() == 0 + && !Bot.Hand.Any(c => !c.IsCode(_CardId.InfiniteImpermanence) && (c.IsTrap() || c.HasType(CardType.QuickPlay))) + && Bot.Hand.Count() <= 6) + { + return false; + } + + if (Card.IsCode(CardId.RyzealPlugIn)) + { + bool targetFlag = Bot.Graveyard.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && (c.Level == 4 || c.IsCanRevive())); + targetFlag |= Bot.Banished.Any(c => c != null && c.IsFaceup() && c.HasSetcode(SetcodeRyzeal) && (c.Level == 4 || c.IsCanRevive())); + if (!targetFlag) + { + return false; + } + } + + List avoid_list = new List(); + int setForInfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setForInfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setForInfiniteImpermanence); + return true; + } + else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } + else + { + SelectSTPlace(); + } + return true; + } + + else if (Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true) || Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true)) + { + if (Card.IsSpell() && !Bot.HasInSpellZone(Card.Id)) + { + SelectSTPlace(); + return true; + } + } + + return false; + } + + public bool ChangePositionFirst() + { + if (Card.IsFacedown() && Card.Level == 4) + { + return true; + } + + if (Enemy.MonsterZone.Any(c => c != null && c.HasPosition(CardPosition.FaceUpDefence) && !c.IsDisabled() && c.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir))) + { + return false; + } + + if (Card.IsCode(_CardId.Number41BagooskatheTerriblyTiredTapir)) + { + bool haveDangerMonster = Enemy.MonsterZone.Any(c => c != null && c.IsFloodgate() && !c.IsDisabled()); + if (Card.IsDefense()) + { + return !haveDangerMonster && !Util.IsTurn1OrMain2(); + } else + { + return haveDangerMonster || Util.IsTurn1OrMain2(); + } + } + + return false; + } + + protected override bool DefaultSetForDiabellze() + { + if (base.DefaultSetForDiabellze()) + { + SelectSTPlace(Card, true); + return true; + } + return false; + } + } +} diff --git a/Game/AI/Decks/ST1732Executor.cs b/Game/AI/Decks/ST1732Executor.cs index a169ea39..7cd50fe7 100644 --- a/Game/AI/Decks/ST1732Executor.cs +++ b/Game/AI/Decks/ST1732Executor.cs @@ -153,6 +153,7 @@ public override void OnNewTurn() { // reset BalancerLordUsed = false; + base.OnNewTurn(); } public override int OnSelectOption(IList options) diff --git a/Game/AI/Decks/SalamangreatExecutor.cs b/Game/AI/Decks/SalamangreatExecutor.cs index daec71cc..46618b99 100644 --- a/Game/AI/Decks/SalamangreatExecutor.cs +++ b/Game/AI/Decks/SalamangreatExecutor.cs @@ -594,6 +594,7 @@ private bool Foxy_activate() } else { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Bot.HasInHand(CardId.Spinny) || FalcoToGY(false)) { if (Bot.HasInHand(CardId.Spinny) && !Bot.HasInGraveyard(CardId.Spinny)) @@ -779,10 +780,12 @@ private bool Rage_activate() public bool G_activate() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return (Duel.Player == 1); } public bool Hand_act_eff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return (Duel.LastChainPlayer == 1); } diff --git a/Game/AI/Decks/SkyStrikerExecutor.cs b/Game/AI/Decks/SkyStrikerExecutor.cs index d7eb1168..634c623e 100644 --- a/Game/AI/Decks/SkyStrikerExecutor.cs +++ b/Game/AI/Decks/SkyStrikerExecutor.cs @@ -146,6 +146,7 @@ public override void OnNewTurn() ShizukuSummoned = false; HayateSummoned = false; WidowAnchorTarget = null; + base.OnNewTurn(); } public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) @@ -194,6 +195,7 @@ public override bool OnSelectYesNo(long desc) private bool MaxxCEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Duel.Player == 1; } @@ -667,6 +669,7 @@ private bool SummonSorceressEffect() private bool JetSynchronEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Bot.HasInMonstersZone(CardId.Raye) || Bot.HasInMonstersZone(CardId.CrystronNeedlefiber)) { AI.SelectCard(GetDiscardHand()); diff --git a/Game/AI/Decks/SuperheavySamuraiExecutor.cs b/Game/AI/Decks/SuperheavySamuraiExecutor.cs new file mode 100644 index 00000000..75b3272b --- /dev/null +++ b/Game/AI/Decks/SuperheavySamuraiExecutor.cs @@ -0,0 +1,1219 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using System; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +namespace WindBot.Game.AI.Decks +{ + [Deck("SuperheavySamurai", "AI_SuperheavySamurai")] + public class SuperheavySamuraiExecutor : DefaultExecutor + { + public class CardId + { + public const int Benkei = 19510093;//弁庆 + public const int Wagon = 34496660;//大八 + public const int Soulpiercer = 90361010;//岩融 + public const int Wakaushi = 82112494;//牛若 + public const int Scales = 78391364;//天秤 + public const int Booster = 56727340;//地铠 + public const int Motorbike = 83334932;//摩托 + public const int Soulhorns = 14624296;//双角 + public const int Soulpeacemaker = 95500396;//仲裁 + + public const int Regulus = 10604644;//轩辕十四 + + public const int MaxxG = 23434538;//增殖的G + public const int JoyousSpring = 14558127;//灰流丽 + public const int PsyFrameDriver = 49036338;//PSY骨架驱动者 + public const int PsyFramegearGamma = 38814750;//PSY骨架装备·γ + public const int EffectVeiler = 97268402;//效果遮蒙者 + public const int HauntedMansion = 73642296;//屋敷童 + public const int SnowRabbit = 59438930;//幽鬼兔 + public const int LockBird = 94145021;//小丑与锁鸟 + + //extra + public const int Masurawo = 64193046;//益荒男 + public const int Fleur = 84815190;//鲜花女男爵 + public const int ASStardustDragon = 30983281;//加速同调星尘龙 + public const int StardustDragon = 30983281;//星尘龙 + public const int SavageDragon = 27548199;//狞猛龙 + public const int Sarutobi = 76471944;//猿飞 + public const int PSYFramelordOmega = 74586817;//PSY骨架王·Ω + public const int GearGigant = 28912357;//齿轮齿巨人 + public const int Unicorn = 38342335;//独角兽 + public const int Elf = 27381364;//卫星闪灵·淘气精灵 + public const int Genius = 22423493;//路径灵 + public const int IP = 65741786;//I:P伪装舞会莱娜 + public const int Scarecrow = 33918636;//案山子 + + } + + private bool normal_summon = false; + private bool p_summoned = false; + private bool p_summoning = false; + private bool activate_Motorbike = false;//摩托 + private bool activate_Wakaushi = false;//神童 + private bool activate_Scales = false;//天秤 + private bool activate_Wagon = false;//大巴 + private bool activate_Booster = false;//地铠 + private bool activate_Soulpeacemaker = false;//仲裁 + private bool activate_Benkei = false;//弁庆 + private bool need_Gear = false;//齿轮齿巨人 + //案山子 + private bool activate_Scarecrow=false; + private bool summon_Scarecrow=false; + private bool summon_Scarecrow2=true; + private bool activate_Sarutobi = false;//猿飞 + private bool activate_Genius = false;//路径灵 + //淘气精灵 + private bool activate_Elf = false; + private bool summon_Elf = false; + //手坑 + private bool activate_MaxxG = false;//增殖的G + private bool activate_PSY = false;//PSY + private bool activate_LockBird = false;//小丑与锁鸟 + private bool to_deck = false; + + public SuperheavySamuraiExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + AddExecutor(ExecutorType.Repos, MonsterRepos); + AddExecutor(ExecutorType.Activate, CardId.PSYFramelordOmega,PSYFunction); + AddExecutor(ExecutorType.Activate, CardId.IP,IPFunction); + AddExecutor(ExecutorType.Activate, CardId.Sarutobi,SarutobiFunction); + AddExecutor(ExecutorType.Activate, CardId.Unicorn,UnicornFunction); + AddExecutor(ExecutorType.Activate, CardId.MaxxG,MaxxCFunction); + AddExecutor(ExecutorType.Activate, CardId.JoyousSpring,DefaultAshBlossomAndJoyousSpring); + AddExecutor(ExecutorType.Activate, CardId.SnowRabbit,DefaultGhostOgreAndSnowRabbit); + AddExecutor(ExecutorType.Activate, CardId.EffectVeiler,DefaultBreakthroughSkill); + AddExecutor(ExecutorType.Activate, CardId.LockBird,LockBirdFunction); + AddExecutor(ExecutorType.Activate, CardId.PsyFramegearGamma,FunctionInHand); + AddExecutor(ExecutorType.Activate, CardId.HauntedMansion,FunctionInHand); + AddExecutor(ExecutorType.Activate, CardId.Masurawo,MasurawoFunction); + AddExecutor(ExecutorType.Activate, CardId.Genius,GeniusFunction); + + //Motorbike's Effect + AddExecutor(ExecutorType.Activate, CardId.Motorbike,MotorbikeFunction); + + //Scales's Effect + AddExecutor(ExecutorType.SpSummon, CardId.Scales); + AddExecutor(ExecutorType.Activate, CardId.Scales,ScalesFunction); + + //Synchron + AddExecutor(ExecutorType.SpSummon, CardId.PSYFramelordOmega,PSYFramelordOmegaSynchronFunction); + + //Pendulum + AddExecutor(ExecutorType.Activate, CardId.Wakaushi,WakaushiFunction); + AddExecutor(ExecutorType.Activate, CardId.Wakaushi,WakaushiEffectFunction); + AddExecutor(ExecutorType.Activate, CardId.Benkei,BenkeiFunction); + AddExecutor(ExecutorType.Activate, CardId.Benkei,BenkeiEffectFunction); + + //Normal Summon & Effect + AddExecutor(ExecutorType.Summon, CardId.Soulpiercer,NormalSummonFunction); + AddExecutor(ExecutorType.Activate, CardId.Soulpiercer,SoulpiercerFunction); + + AddExecutor(ExecutorType.Summon, CardId.Wagon,NormalSummonFunction); + AddExecutor(ExecutorType.Activate, CardId.Wagon,WagonFunction); + AddExecutor(ExecutorType.Activate, CardId.Wagon,WagonFunction); + + AddExecutor(ExecutorType.Summon, CardId.Booster,BoosterNormalSummonFunction); + AddExecutor(ExecutorType.Summon, CardId.Scales,ScalesNormalSummonFunction); + + //boost & Gear + AddExecutor(ExecutorType.Activate, CardId.Booster,BoosterEquipFunction); + AddExecutor(ExecutorType.Activate, CardId.Booster,BoosterFunction); + AddExecutor(ExecutorType.SpSummon, CardId.GearGigant,GearGigantXyzFunction); + AddExecutor(ExecutorType.Activate, CardId.GearGigant,GearGigantFunction); + + //equip Soulpiercer + AddExecutor(ExecutorType.Activate, CardId.Soulpiercer,SoulpiercerEquipFunction); + + //Link Scarecrow + AddExecutor(ExecutorType.SpSummon, CardId.Scarecrow,ScarecrowLinkFunction); + AddExecutor(ExecutorType.Activate, CardId.Scarecrow,ScarecrowFunction); + AddExecutor(ExecutorType.SpSummon, CardId.Scarecrow,ScarecrowLinkFunction2); + + //Synchron + AddExecutor(ExecutorType.SpSummon, CardId.ASStardustDragon,ASStardustDragonSynchronFunction); + + //Effect After Synchron + AddExecutor(ExecutorType.Activate, CardId.SavageDragon,SavageDragonFunction); + AddExecutor(ExecutorType.Activate, CardId.ASStardustDragon,ASStardustDragonFunction); + + //Wakaushi's Effect After Synchron + AddExecutor(ExecutorType.Activate, CardId.Wakaushi,WakaushiReturnPFunction); + + //Synchron + AddExecutor(ExecutorType.SpSummon, CardId.Fleur,FleurSynchronFunction); + AddExecutor(ExecutorType.Activate, CardId.Fleur,FleurFunction); + + //equip Soulpeacemaker + AddExecutor(ExecutorType.Activate, CardId.Soulpeacemaker,SoulpeacemakerEquipFunction); + AddExecutor(ExecutorType.Activate, CardId.Soulpeacemaker,SoulpeacemakerFunction); + + //Link + AddExecutor(ExecutorType.SpSummon, CardId.Genius,GeniusLinkFunction); + + AddExecutor(ExecutorType.SpSummon, Psummon); + + //Link + AddExecutor(ExecutorType.SpSummon, CardId.Elf,ElfLinkFunction); + AddExecutor(ExecutorType.Activate, CardId.Elf,ElfFunction); + + AddExecutor(ExecutorType.Activate, CardId.Motorbike,MotorbikeFunction); + + //Synchron + AddExecutor(ExecutorType.SpSummon, CardId.SavageDragon,SavageDragonSynchronFunction); + + //Link + AddExecutor(ExecutorType.SpSummon, CardId.IP,IPLinkFunction); + + //Regulus's Effect + AddExecutor(ExecutorType.Activate, CardId.Regulus,RegulusFunction); + + //booster + AddExecutor(ExecutorType.Activate, CardId.Booster,BoosterEquipFunction2); + AddExecutor(ExecutorType.Activate, CardId.Booster,BoosterFunction); + AddExecutor(ExecutorType.Activate, CardId.Booster,BoosterEquipFunction3); + AddExecutor(ExecutorType.Activate, CardId.Booster,BoosterFunction); + + //Synchron + AddExecutor(ExecutorType.SpSummon, CardId.Masurawo,MasurawoSynchronFunction); + AddExecutor(ExecutorType.SpSummon, CardId.Sarutobi,DeSynchronFunction); + + //equip Soulhorns + AddExecutor(ExecutorType.Activate, CardId.Soulhorns,SoulhornsEquipFunction); + + } + public override void OnNewTurn() + { + normal_summon = false; + p_summoned = false; + p_summoning = false; + activate_Motorbike = false; + activate_Wakaushi = false; + activate_Scales = false; + activate_Wagon = false; + activate_Booster = false; + activate_Soulpeacemaker = false; + activate_Benkei = false; + need_Gear = false; + activate_Scarecrow=false; + summon_Scarecrow=false; + summon_Scarecrow2=true; + activate_Elf = false; + summon_Elf = false; + activate_MaxxG = false; + activate_PSY = false; + activate_LockBird = false; + activate_Genius = false; + activate_Sarutobi = false; + to_deck = false; + base.OnNewTurn(); + } + public override bool OnSelectHand() + { + return true; + } + private bool MonsterRepos() + { + if (Card.IsFacedown()) + return true; + if (Card.IsFaceup() && Card.IsAttack() && (Card.Id == CardId.Masurawo || Card.Id == CardId.Sarutobi)) + return true; + return false; + } + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (cardId == CardId.Masurawo || cardId == CardId.Sarutobi) + return CardPosition.FaceUpDefence; + } + return 0; + } + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone) + { + if (cardId == CardId.Scarecrow) + { + int a=(Zones.z6 & available); + int b=(Zones.z5 & available); + if (Bot.MonsterZone[2] != null && Bot.MonsterZone[2].Controller == 0 && !FinalCards(Bot.MonsterZone[2].Id)) + a = 0; + else if (Bot.MonsterZone[0] != null && Bot.MonsterZone[0].Controller == 0 && !FinalCards(Bot.MonsterZone[0].Id)) + b = 0; + if (b > 0) return Zones.z5; + if (a > 0) return Zones.z6; + } + else if (cardId == CardId.Unicorn || cardId == CardId.Elf || cardId == CardId.IP) + { + if ((Zones.z6 & available) > 0) return Zones.z6; + if ((Zones.z5 & available) > 0) return Zones.z5; + } + else if (cardId == CardId.Genius) + { + int a=(Zones.z6 & available); + int b=(Zones.z5 & available); + if (Bot.MonsterZone[4] != null && Bot.MonsterZone[4].Controller == 0 && !FinalCards(Bot.MonsterZone[4].Id)) + a = 0; + else if (Bot.MonsterZone[0] != null && Bot.MonsterZone[0].Controller == 0 && !FinalCards(Bot.MonsterZone[0].Id)) + b = 0; + if (a > 0) return Zones.z6; + if (b > 0) return Zones.z5; + } + else if (cardId == CardId.Regulus || cardId == CardId.GearGigant) + { + if ((Zones.z3 & available) > 0) return Zones.z3; + } + else + { + if ((Zones.z1 & available) > 0) return Zones.z1; + if ((Zones.z4 & available) > 0) return Zones.z4; + if ((Zones.z2 & available) > 0) return Zones.z2; + if ((Zones.z3 & available) > 0) return Zones.z3; + if ((Zones.z0 & available) > 0) return Zones.z0; + } + } + return base.OnSelectPlace(cardId, player, location, available); + } + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + if (AI.HaveSelectedCards()) return null; + if (p_summoning || ((Card == Bot.SpellZone[0] || Card == Bot.SpellZone[4]) && hint == HintMsg.SpSummon && + Card.HasType(CardType.Pendulum))) + { + List result = new List(); + List scards = cards.Where(card => card != null && card.HasSetcode(0x9a) && card.Level == 4).ToList(); + if (scards.Count <2) scards = cards.Where(card => card != null && card.HasSetcode(0x9a)).ToList(); + p_summoning = false; + if (scards.Count > 0) return Util.CheckSelectCount(result, scards, 1, 1); + else if (min == 0) return result; // empty + } + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + private List GetZoneCards(CardLocation loc, ClientField player) + { + List res = new List(); + List temp = new List(); + if ((loc & CardLocation.Hand) > 0) { temp = player.Hand.Where(card => card != null).ToList(); if (temp.Count() > 0) res.AddRange(temp); } + if ((loc & CardLocation.MonsterZone) > 0) { temp = player.GetMonsters(); if (temp.Count() > 0) res.AddRange(temp); } + if ((loc & CardLocation.SpellZone) > 0) { temp = player.GetSpells(); if (temp.Count() > 0) res.AddRange(temp); } + if ((loc & CardLocation.Grave) > 0) { temp = player.Graveyard.Where(card => card != null).ToList(); if (temp.Count() > 0) res.AddRange(temp); } + if ((loc & CardLocation.Removed) > 0) { temp = player.Banished.Where(card => card != null).ToList(); if (temp.Count() > 0) res.AddRange(temp); } + if ((loc & CardLocation.Extra) > 0) { temp = player.ExtraDeck.Where(card => card != null).ToList(); if (temp.Count() > 0) res.AddRange(temp); } + return res; + } + private bool FinalCards(int cname) + { + int[] cardsname = new[] {CardId.Masurawo,CardId.Fleur,CardId.SavageDragon,CardId.Sarutobi,CardId.Regulus,CardId.IP}; + foreach(var cardname in cardsname) + { + if (cname == cardname) return true; + } + return false; + } + private bool TurnerCards(int cname) + { + int[] cardsname =new[] {CardId.PsyFramegearGamma,CardId.Wakaushi,CardId.Motorbike}; + foreach(var cardname in cardsname) + { + if (cname == cardname) return true; + } + return false; + } + private bool Psummon() + { + List cards = GetZoneCards(CardLocation.Hand, Bot).Where(card => card != null && card.HasSetcode(0x9a) && card.Level > 1 && card.Level < 8).ToList(); + if (cards.Count > 0 && Card.Location == CardLocation.SpellZone) + { + p_summoning = true; + p_summoned = true; + return true; + } + return false; + } + private bool MaxxCFunction() + { + activate_MaxxG = true; + return DefaultMaxxC() && !activate_LockBird; + } + private bool FunctionInHand() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + return Duel.LastChainPlayer == 1; + } + private bool LockBirdFunction() + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + if (Duel.Player == 0 || activate_LockBird) + { + return false; + } + activate_LockBird = true; + return !activate_MaxxG; + } + private bool MotorbikeFunction() + { + if (Card.Location == CardLocation.Hand) + { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; + int targetid = -1; + List cards = GetZoneCards(CardLocation.MonsterZone, Bot).Where(card => card != null && card.IsFaceup()).ToList(); + if (!(Bot.HasInHand(CardId.Wakaushi) || Bot.HasInMonstersZone(CardId.Wakaushi) || Bot.HasInSpellZone(CardId.Wakaushi)) && !activate_Wakaushi) + { + targetid = CardId.Wakaushi; + } + else if (cards.Count() == 0 && !normal_summon) + { + targetid = CardId.Soulpiercer; + } + else if (!Bot.HasInHand(CardId.Soulpeacemaker) && !Bot.HasInSpellZone(CardId.Soulpeacemaker) && !activate_Soulpeacemaker && (normal_summon || Bot.HasInMonstersZone(CardId.Scarecrow))) + { + targetid = CardId.Soulpeacemaker; + } + else + { + targetid = CardId.Soulpiercer; + } + if (targetid > 0) AI.SelectCard(targetid); + activate_Motorbike = true; + return true; + } + else if (Card.Location == CardLocation.MonsterZone && activate_Elf) + { + AI.SelectCard(Card); + activate_Elf = false; + return true; + } + return false; + } + private bool BoosterNormalSummonFunction() + { + List cards = Bot.Hand.GetMonsters().Where(card => card != null && card.Id == CardId.Booster).ToList(); + return (NormalSummonFunction() && !activate_Booster && cards.Count >= 2); + } + private bool ScalesNormalSummonFunction() + { + return (NormalSummonFunction() && (Bot.HasInGraveyard(new[] { + CardId.Soulpiercer, + CardId.Motorbike, + CardId.Wakaushi, + CardId.Wagon, + CardId.Booster, + }) || (Bot.HasInHand(CardId.Booster) && !activate_Booster))); + } + private bool NormalSummonFunction() + { + normal_summon = true; + return DefaultMonsterSummon(); + } + private bool ScalesFunction() + { + AI.SelectCard(new[] { + CardId.Soulpiercer, + CardId.Motorbike, + CardId.Wakaushi, + CardId.Wagon, + CardId.Booster, + }); + activate_Scales = true; + return true; + } + private bool WagonFunction() + { + if (ActivateDescription == Util.GetStringId(CardId.Wagon, 0)) + return Card.IsAttack(); + if (ActivateDescription == Util.GetStringId(CardId.Wagon, 1)) + { + int targetid = -1; + if (!(Bot.HasInHand(CardId.Soulpiercer)||Bot.HasInMonstersZone(CardId.Soulpiercer))) + { + targetid = CardId.Soulpiercer; + } + else if (!Bot.HasInHand(CardId.Soulpeacemaker) && !activate_Soulpeacemaker) + { + targetid = CardId.Soulpeacemaker; + } + else if (!Bot.HasInHand(CardId.Booster) && !activate_Booster) + { + targetid = CardId.Booster; + } + if (targetid > 0) AI.SelectCard(targetid); + activate_Wagon = true; + return true; + } + else + { + return true; + } + } + private bool SoulpiercerFunction() + { + if (Card.Location == CardLocation.Grave) + { + int CardCount = GetZoneCards(CardLocation.Hand, Bot).Count(card => card != null && card.HasSetcode(0x9a) && card.Level >= 2 && card.Level <= 7); + int targetid = -1; + if (!Bot.HasInHand(CardId.Motorbike) && !activate_Motorbike) + { + targetid = CardId.Motorbike; + } + else if (!(Bot.HasInHand(CardId.Wakaushi) || Bot.HasInMonstersZone(CardId.Wakaushi) || Bot.HasInSpellZone(CardId.Wakaushi)) && !activate_Wakaushi) + { + targetid = CardId.Wakaushi; + } + else if (!Bot.HasInHand(CardId.Soulpeacemaker) && !activate_Soulpeacemaker) + { + targetid = CardId.Soulpeacemaker; + } + else if (!Bot.HasInHand(CardId.Scales) && !activate_Scales && (!normal_summon || !p_summoned) && (activate_Soulpeacemaker || (!Bot.HasInHand(CardId.Soulpeacemaker) && !Bot.HasInSpellZone(CardId.Soulpeacemaker)))) + { + targetid = CardId.Scales; + } + else if (!Bot.HasInHand(CardId.Wagon) && !activate_Wagon) + { + targetid = CardId.Wagon; + } + else if (CardCount < 2 && !p_summoned) + { + targetid = CardId.Wakaushi; + } + else if (!Bot.HasInHand(CardId.Booster) && !activate_Booster) + { + targetid = CardId.Booster; + } + else if (!Bot.HasInHand(CardId.Soulhorns) && !Bot.HasInSpellZone(CardId.Soulhorns) && (Bot.HasInMonstersZone(CardId.Sarutobi) || Bot.HasInMonstersZone(CardId.Masurawo))) + { + targetid = CardId.Soulhorns; + } + else {targetid = CardId.Wakaushi;} + if (targetid > 0) AI.SelectCard(targetid); + return true; + } + return false; + } + private bool WakaushiFunction() + { + if (Card.Location != CardLocation.Hand||Bot.HasInMonstersZone(CardId.Wakaushi)) + return false; + ClientCard l = Util.GetPZone(0, 0); + ClientCard r = Util.GetPZone(0, 1); + if (l == null && r == null) + return true; + if (l == null && r.RScale != Card.LScale) + return true; + if (r == null && l.LScale != Card.RScale) + return true; + return false; + } + private bool WakaushiEffectFunction() + { + if (Card.Location == CardLocation.SpellZone) + { + AI.SelectCard(CardId.Benkei); + activate_Wakaushi = true; + return true; + } + return false; + } + private bool BenkeiFunction() + { + if (Card.Location != CardLocation.Hand || Bot.HasInSpellZone(CardId.Benkei)) return false; + List cards1 = GetZoneCards(CardLocation.Hand, Bot).Where(card => card != null && card.Id == CardId.Benkei).ToList(); + List cards2 = GetZoneCards(CardLocation.Removed, Bot).Where(card => card != null && card.Id == CardId.Benkei).ToList(); + if (cards1.Count() >= 2 || Bot.HasInGraveyard(CardId.Benkei) || Bot.HasInExtra(CardId.Benkei) || cards2.Count() > 0) + return true; + return false; + } + private bool BenkeiEffectFunction() + { + if (Card.Location == CardLocation.SpellZone) + { + int targetid = -1; + if (!(Bot.HasInHand(CardId.Soulpiercer) || Bot.HasInMonstersZone(CardId.Soulpiercer) || Bot.HasInSpellZone(CardId.Soulpiercer)) && !(Bot.HasInMonstersZone(CardId.Scarecrow) && !activate_Soulpeacemaker)) + { + targetid = CardId.Soulpiercer; + } + else if (!Bot.HasInHand(CardId.Soulpeacemaker) && !activate_Soulpeacemaker) + { + targetid = CardId.Soulpeacemaker; + } + else if (!Bot.HasInHand(CardId.Booster) && !activate_Booster) + { + targetid = CardId.Booster; + } + if (targetid > 0) AI.SelectCard(targetid); + activate_Benkei = true; + return true; + } + return false; + } + private bool WakaushiReturnPFunction() + { + if (Card.Location == CardLocation.Extra||Card.Location == CardLocation.Removed) + { + ClientCard l = Util.GetPZone(0, 0); + ClientCard r = Util.GetPZone(0, 1); + if (l == null && r == null) + return true; + if (l == null && r.RScale != Card.LScale) + return true; + if (r == null && l.LScale != Card.RScale) + return true; + } + return false; + } + private bool MasurawoFunction() + { + if (ActivateDescription == 96) + { + List cards = GetZoneCards(CardLocation.SpellZone, Bot).Where(card => card != null && card.HasSetcode(0x9a)).ToList(); + if (cards.Count > 0) + { + AI.SelectCard(cards); + return true; + } + else + { + cards = GetZoneCards(CardLocation.MonsterZone, Bot).Where(card => card != null && card.HasSetcode(0x9a) && !FinalCards(card.Id)).ToList(); + if (cards.Count > 0) + { + AI.SelectCard(cards); + return true; + } + } + } + return true; + + } + private bool MasurawoSynchronFunction() + { + bool chk = true; + if (Bot.HasInMonstersZone(CardId.ASStardustDragon) || Bot.HasInMonstersZone(CardId.Benkei)) + chk = false; + var materials_lists = Util.GetSynchroMaterials(Bot.MonsterZone,12,1,1,false,chk,null, + card => { return !FinalCards(card.Id); }); + if (materials_lists.Count <= 0) return false; + AI.SelectMaterials(materials_lists[0]); + AI.SelectPosition(CardPosition.FaceUpDefence); + return true; + } + private bool FleurSynchronFunction() + { + bool chk = true; + if (Bot.HasInMonstersZone(CardId.Motorbike) && (Bot.HasInMonstersZone(CardId.ASStardustDragon) || Bot.HasInMonstersZone(CardId.Benkei))) + chk = false; + var materials_lists = Util.GetSynchroMaterials(Bot.MonsterZone,10,1,1,false,chk,null, + card => { return !FinalCards(card.Id); }); + if (materials_lists.Count <= 0) return false; + AI.SelectMaterials(materials_lists[0]); + return true; + } + private bool DeSynchronFunction() + { + AI.SelectPosition(CardPosition.FaceUpDefence); + return true; + } + private bool SavageDragonSynchronFunction() + { + if (Bot.HasInGraveyard(new[] { + CardId.Scarecrow, + CardId.IP, + CardId.Genius, + CardId.Unicorn, + CardId.Elf, + })) + { + return true; + } + return false; + } + private bool ASStardustDragonSynchronFunction() + { + if (Bot.HasInGraveyard(CardId.Motorbike) || Bot.HasInGraveyard(CardId.PsyFramegearGamma)) + { + return (Bot.HasInExtra(CardId.Fleur) || Bot.HasInExtra(CardId.Masurawo)); + } + else if (Bot.HasInMonstersZone(CardId.Motorbike)) + { + AI.SelectMaterials(CardId.Motorbike); + return true; + } + else if (Bot.HasInMonstersZone(CardId.PsyFramegearGamma)) + { + AI.SelectMaterials(CardId.PsyFramegearGamma); + return true; + } + return false; + } + private bool PSYFramelordOmegaSynchronFunction() + { + if (Bot.HasInMonstersZone(CardId.Motorbike)) + AI.SelectMaterials(CardId.Motorbike); + else if (Bot.HasInMonstersZone(CardId.PsyFramegearGamma)) + AI.SelectMaterials(CardId.PsyFramegearGamma); + return activate_PSY || activate_Scales; + } + private bool SavageDragonFunction() + { + if (Duel.LastChainPlayer == 1) + return true; + AI.SelectCard(new[] + { + CardId.Unicorn, + CardId.Genius, + CardId.Elf, + CardId.IP, + CardId.Scarecrow + }); + return true; + } + private bool ASStardustDragonFunction() + { + if (Duel.LastChainPlayer == 1 && ActivateDescription == Util.GetStringId(CardId.ASStardustDragon, 1)) + { + return true; + } + else if (ActivateDescription == Util.GetStringId(CardId.ASStardustDragon, 0)) + { + + int targetid = -1; + if (Bot.HasInGraveyard(CardId.Motorbike)) + { + targetid = CardId.Motorbike; + } + else if (Bot.HasInGraveyard(CardId.PsyFramegearGamma)) + { + targetid = CardId.PsyFramegearGamma; + } + if (targetid > 0) AI.SelectCard(targetid); + if (targetid == CardId.Motorbike && !Bot.HasInExtra(CardId.Fleur) && Bot.HasInExtra(CardId.Masurawo)) + activate_Elf = true; + return true; + } + return false; + } + private bool ScarecrowLinkFunction() + { + List material = new List(); + List cards = Bot.GetMonstersInExtraZone().Where(card => card != null && card.Id == CardId.Scarecrow).ToList(); + if ((cards.Count() > 0 && !summon_Scarecrow) || summon_Scarecrow || activate_Scarecrow) return false; + int targetid = -1; + if (Bot.MonsterZone[0] != null && Bot.MonsterZone[2] != null) { + if (Bot.MonsterZone[0].Id == CardId.Soulpiercer) material.Add(Bot.MonsterZone[0]); + else if (Bot.MonsterZone[2].Id == CardId.Soulpiercer) material.Add(Bot.MonsterZone[2]); + else if (!FinalCards(Bot.MonsterZone[0].Id) && Bot.MonsterZone[0].HasSetcode(0x9a)) material.Add(Bot.MonsterZone[0]); + else if (!FinalCards(Bot.MonsterZone[2].Id) && Bot.MonsterZone[2].HasSetcode(0x9a)) material.Add(Bot.MonsterZone[2]); + } + else if (Bot.HasInMonstersZone(CardId.Soulpiercer)) + { + targetid = CardId.Soulpiercer; + } + else if (Bot.HasInMonstersZone(CardId.Wagon)) + { + targetid = CardId.Wagon; + } + if (material.Count > 0) AI.SelectMaterials(material); + else if (targetid > 0) AI.SelectMaterials(targetid); + summon_Scarecrow=true; + return (Bot.HasInGraveyard(new[] { + CardId.Soulpiercer, + CardId.Wakaushi, + CardId.Benkei, + CardId.Wagon, + })||Bot.HasInMonstersZone(new[] { + CardId.Soulpiercer, + CardId.Wagon, + CardId.Wakaushi, + })); + } + private bool DragonRavineField() + { + if (Card.Location == CardLocation.Hand) + return DefaultField(); + return false; + } + + private bool ScarecrowFunction() + { + int tributeId = -1; + if (Bot.HasInHand(CardId.PsyFrameDriver)) + {tributeId = CardId.PsyFrameDriver;} + else if (Bot.HasInHand(CardId.PsyFramegearGamma)) + {tributeId = CardId.PsyFramegearGamma;} + else if (Bot.HasInHand(CardId.Benkei)) + {tributeId = CardId.Benkei;} + else if (Bot.HasInHand(CardId.HauntedMansion)) + {tributeId = CardId.HauntedMansion;} + else if (Bot.HasInHand(CardId.EffectVeiler)) + {tributeId = CardId.EffectVeiler;} + else if (Bot.HasInHand(CardId.SnowRabbit)) + {tributeId = CardId.SnowRabbit;} + else if (Bot.HasInHand(CardId.JoyousSpring)) + {tributeId = CardId.JoyousSpring;} + else if (Bot.HasInHand(CardId.Booster)) + {tributeId = CardId.Booster;} + else if (Bot.HasInHand(CardId.Wagon)) + {tributeId = CardId.Wagon;} + else if (Bot.HasInHand(CardId.Scales)) + {tributeId = CardId.Scales;} + else if (Bot.HasInHand(CardId.LockBird)) + {tributeId = CardId.LockBird;} + else if (Bot.HasInHand(CardId.MaxxG)) + {tributeId = CardId.MaxxG;} + int needId = -1; + if (Bot.HasInGraveyard(CardId.Soulpiercer)) + { + if (Bot.HasInGraveyard(CardId.Scales) && !activate_Scales) + {needId = CardId.Scales;} + else + {needId = CardId.Soulpiercer;} + } + else if (Bot.HasInGraveyard(CardId.Masurawo)) + {needId = CardId.Masurawo;} + else if (Bot.HasInGraveyard(CardId.Sarutobi)) + {needId = CardId.Sarutobi;} + else if (Bot.HasInMonstersZone(CardId.Soulpiercer)) + { + if (Bot.HasInGraveyard(CardId.Wakaushi)) + {needId = CardId.Wakaushi;} + if (Bot.HasInGraveyard(CardId.Motorbike)) + {needId = CardId.Motorbike;} + } + if (GetZoneCards(CardLocation.Hand, Bot).Count(card => card != null && card.Id == CardId.Scales) + GetZoneCards(CardLocation.Grave, Bot).Count(card => card != null && card.Id == CardId.Scales) + GetZoneCards(CardLocation.Onfield, Bot).Count(card => card != null && card.Id == CardId.Scales) == 2 && GetZoneCards(CardLocation.Hand, Bot).Count(card => card != null && card.Id == CardId.Scales)>=1 && !activate_Scales) + { + tributeId = CardId.Scales; + needId = CardId.Scales; + } + AI.SelectCard(tributeId); + AI.SelectNextCard(needId); + if (((!Bot.HasInHand(CardId.Wakaushi) && !Bot.HasInSpellZone(CardId.Wakaushi)) || activate_Wakaushi) + && (!Bot.HasInHand(CardId.Motorbike) || activate_Motorbike) + && ((!Bot.HasInHand(CardId.Soulpeacemaker) && !Bot.HasInSpellZone(CardId.Soulpeacemaker)) || activate_Soulpeacemaker) + && (!Bot.HasInSpellZone(CardId.Benkei) || activate_Benkei) + && (needId == CardId.Soulpiercer) + && (!activate_Wakaushi || !activate_Motorbike || !activate_Soulpeacemaker || !activate_Benkei) + ) + { + summon_Scarecrow2 = false; + } + activate_Scarecrow = true; + return true; + } + private bool ScarecrowLinkFunction2() + { + if (!summon_Scarecrow2) + { + summon_Scarecrow2 = true; + return true; + } + return false; + } + private bool UnicornFunction() + { + List Enemycards = GetZoneCards(CardLocation.Onfield,Enemy); + if (Bot.Hand.Count == 0 || Enemycards.Count(card => card != null && !card.IsShouldNotBeTarget()) == 0) + { + if (to_deck) to_deck = false; + return false; + } + int tributeId = -1; + if (Bot.HasInHand(CardId.PsyFrameDriver)) + {tributeId = CardId.PsyFrameDriver;} + else if (Bot.HasInHand(CardId.PsyFramegearGamma)) + {tributeId = CardId.PsyFramegearGamma;} + else if (Bot.HasInHand(CardId.Benkei)) + {tributeId = CardId.Benkei;} + else if (Bot.HasInHand(CardId.HauntedMansion)) + {tributeId = CardId.HauntedMansion;} + else if (Bot.HasInHand(CardId.EffectVeiler)) + {tributeId = CardId.EffectVeiler;} + else if (Bot.HasInHand(CardId.SnowRabbit)) + {tributeId = CardId.SnowRabbit;} + else if (Bot.HasInHand(CardId.JoyousSpring)) + {tributeId = CardId.JoyousSpring;} + else if (Bot.HasInHand(CardId.Booster)) + {tributeId = CardId.Booster;} + else if (Bot.HasInHand(CardId.Wagon)) + {tributeId = CardId.Wagon;} + else if (Bot.HasInHand(CardId.Scales)) + {tributeId = CardId.Scales;} + else if (Bot.HasInHand(CardId.LockBird)) + {tributeId = CardId.LockBird;} + else if (Bot.HasInHand(CardId.MaxxG)) + {tributeId = CardId.MaxxG;} + if (to_deck) to_deck = false; + AI.SelectCard(tributeId); + return true; + } + private bool BoosterEquipFunction() + { + if (Card.Location != CardLocation.Hand || activate_Booster) + return false; + List ChkCardsHand = Bot.Hand.GetMonsters().ToList(); + foreach (var card in ChkCardsHand) + { + if (card.Id == CardId.Motorbike && !activate_Motorbike) return false; + else if (card.Id == CardId.Soulpiercer) return false; + else if (card.Id == CardId.Soulpeacemaker && !activate_Soulpeacemaker) return false; + else if (card.Id == CardId.Wakaushi && !activate_Wakaushi) return false; + else if (card.Id == CardId.Wagon && (!activate_Wagon || !normal_summon)) return false; + else if (card.Id == CardId.Benkei && !activate_Benkei) return false; + } + List ChkCardsSpell = GetZoneCards(CardLocation.SpellZone,Bot).Where(card => card != null && card.IsFaceup()).ToList(); + foreach (var card in ChkCardsSpell) + { + if (card.Id == CardId.Wakaushi && !activate_Wakaushi) return false; + else if (card.Id == CardId.Soulpiercer) return false; + else if (card.Id == CardId.Soulpeacemaker && !activate_Soulpeacemaker) return false; + else if (card.Id == CardId.Wakaushi && !activate_Wakaushi) return false; + else if (card.Id == CardId.Benkei && !activate_Benkei) return false; + } + List ChkCardsMonster = GetZoneCards(CardLocation.MonsterZone,Bot).Where(card => card != null && card.IsFaceup() && card.Level == 4).ToList(); + if (ChkCardsMonster.Count == 0) return false; + foreach (var card in ChkCardsMonster) + { + if (card.Id == CardId.Soulpiercer) return false; + } + List ChkCardsGrave = GetZoneCards(CardLocation.Grave,Bot).ToList(); + foreach (var card in ChkCardsGrave) + { + if (card.Id == CardId.Soulpiercer && (Bot.HasInMonstersZone(CardId.Scarecrow) || Bot.HasInExtra(CardId.Scarecrow))) return false; + else if (card.Level == 4 && card.HasRace(CardRace.Machine) && Bot.HasInHand(CardId.Scales) && !normal_summon) return false; + } + if (Bot.HasInExtra(CardId.IP) && p_summoned) return true; + need_Gear = true; + return true; + } + private bool BoosterEquipFunction2() + { + if (Bot.HasInExtra(CardId.IP) && p_summoned && !activate_Booster) return true; + return false; + } + private bool BoosterEquipFunction3() + { + List cards = GetZoneCards(CardLocation.MonsterZone,Bot).Where(card => card != null && card.IsFaceup() && !FinalCards(card.Id) && card.Id != CardId.Scarecrow).ToList(); + if (Bot.HasInMonstersZone(CardId.IP) && p_summoned && !activate_Booster && cards.Count() == 0) return true; + return false; + } + private bool BoosterFunction() + { + if (Card.Location == CardLocation.SpellZone) + { + activate_Booster = true; + return true; + } + return false; + } + private bool GearGigantXyzFunction() + { + if (need_Gear) + { + need_Gear = false; + return true; + } + return false; + } + private bool GearGigantFunction() + { + if (Card.Location == CardLocation.MonsterZone) + { + List ChkCards = GetZoneCards(CardLocation.MonsterZone,Bot).Where(card => card != null && card.IsFaceup() && card.HasSetcode(0x9a)).ToList(); + int targetid = -1; + if (!Bot.HasInHand(CardId.Motorbike) && !activate_Motorbike) + { + targetid = CardId.Motorbike; + } + else if (!(Bot.HasInHand(CardId.Wakaushi) || Bot.HasInSpellZone(CardId.Wakaushi)) && !activate_Wakaushi) + { + targetid = CardId.Wakaushi; + } + else if (!Bot.HasInHand(CardId.Soulpiercer) && (!normal_summon || (ChkCards.Count >= 1))) + { + targetid = CardId.Soulpiercer; + } + if (targetid > 0) AI.SelectCard(targetid); + return true; + } + return false; + } + private bool SoulpiercerEquipFunction() + { + if (Card.Location != CardLocation.Hand) + return false; + int tributeId = -1; + if (Bot.HasInMonstersZone(CardId.Wagon)) + {tributeId = CardId.Wagon;} + else if (Bot.HasInMonstersZone(CardId.Wakaushi)) + {tributeId = CardId.Wakaushi;} + AI.SelectCard(tributeId); + return Bot.HasInMonstersZone(new[] { + CardId.Wakaushi, + CardId.Wagon, + }); + } + private bool SoulpeacemakerEquipFunction() + { + if (Card.Location != CardLocation.Hand) + return false; + int tributeId = -1; + List cards = Bot.GetMonstersInExtraZone().Where(card => card != null && card.Id == CardId.Scarecrow).ToList(); + if (cards.Count() > 0) + AI.SelectCard(cards); + else + { + if (Bot.HasInMonstersZone(CardId.Scarecrow)) + {tributeId = CardId.Scarecrow;} + else if (Bot.HasInMonstersZone(CardId.Soulpiercer)) + {tributeId = CardId.Soulpiercer;} + AI.SelectCard(tributeId); + } + return Bot.HasInMonstersZone(new[] { + CardId.Scarecrow, + CardId.Soulpiercer, + }); + } + private bool SoulhornsEquipFunction() + { + if (Card.Location != CardLocation.Hand) + return false; + int tributeId = -1; + if (Bot.HasInMonstersZone(CardId.Masurawo)) + {tributeId = CardId.Masurawo;} + else if (Bot.HasInMonstersZone(CardId.Sarutobi)) + {tributeId = CardId.Sarutobi;} + AI.SelectCard(tributeId); + return Bot.HasInMonstersZone(new[] { + CardId.Masurawo, + CardId.Sarutobi, + }); + } + private bool SoulpeacemakerFunction() + { + if (Card.Location == CardLocation.SpellZone) + { + int tributeId = -1; + if (Bot.HasInMonstersZone(CardId.Soulpiercer)) + {tributeId = CardId.Wakaushi;} + else if (Bot.HasInGraveyard(CardId.Soulpiercer)||!activate_Scales) + {tributeId = CardId.Scales;} + else if (!Bot.HasInGraveyard(CardId.Soulpiercer)||activate_Scales) + {tributeId = CardId.Soulpiercer;} + AI.SelectCard(tributeId); + activate_Soulpeacemaker = true; + return true; + } + return false; + } + private bool GeniusLinkFunction() + { + if ((Bot.MonsterZone[4] != null && Bot.MonsterZone[4].Controller == 0 && !FinalCards(Bot.MonsterZone[4].Id)) && (Bot.MonsterZone[0] != null && Bot.MonsterZone[0].Controller == 0 && !FinalCards(Bot.MonsterZone[0].Id))) + return false; + List Pcards = GetZoneCards(CardLocation.Hand, Bot).Where(card => card != null && card.HasSetcode(0x9a) && card.Level > 1 && card.Level < 8).ToList(); + if (Pcards.Count() < 2 && !Bot.HasInMonstersZone(CardId.Soulpiercer)) return false; + List Rcards = GetZoneCards(CardLocation.Removed, Bot).Where(card => card != null && card.Id == CardId.Regulus).ToList(); + if (Bot.HasInHand(CardId.Regulus) || Bot.HasInGraveyard(CardId.Regulus) || Bot.HasInSpellZone(CardId.Regulus) || Bot.HasInMonstersZone(CardId.Regulus) || Rcards.Count() > 0) return false; + bool linkchk = false; + List materials = new List(); + if (Bot.MonsterZone[6] != null && Bot.MonsterZone[6].Controller == 0 && Bot.MonsterZone[6].Id != CardId.Scarecrow && !FinalCards(Bot.MonsterZone[6].Id)) + { + materials.Add(Bot.MonsterZone[6]); + linkchk = true; + } + else if (Bot.MonsterZone[5] != null && Bot.MonsterZone[5].Controller == 0 && Bot.MonsterZone[5].Id != CardId.Scarecrow && !FinalCards(Bot.MonsterZone[5].Id)) + { + materials.Add(Bot.MonsterZone[5]); + linkchk = true; + } + List cards = Bot.GetMonstersInMainZone().Where(card => card != null && card.IsFaceup() && card.HasRace(CardRace.Machine)).ToList(); + foreach (var card in cards) + { + if (card == null || FinalCards(card.Id)) continue; + else materials.Add(card); + } + if (materials.Count <=1) return false; + AI.SelectMaterials(materials); + return ((Bot.GetMonstersInExtraZone().Count == 0 || linkchk) && !p_summoned && !activate_Genius); + } + private bool GeniusFunction() + { + if (ActivateDescription == Util.GetStringId(CardId.Genius,1)) + { + AI.SelectCard(CardId.Regulus); + activate_Genius = true; + return true; + } + return false; + } + private bool ElfLinkFunction() + { + if (!Bot.HasInGraveyard(CardId.Motorbike)) return false; + List materials = new List(); + if (Bot.MonsterZone[6] != null && Bot.MonsterZone[6].Controller == 0 && !FinalCards(Bot.MonsterZone[6].Id)) + { + materials.Add(Bot.MonsterZone[6]); + } + else if (Bot.MonsterZone[5] != null && Bot.MonsterZone[5].Controller == 0 && !FinalCards(Bot.MonsterZone[5].Id)) + { + materials.Add(Bot.MonsterZone[5]); + } + List TunrerCards = Bot.GetMonstersInMainZone().Where(card => card != null && card.IsFaceup() && TurnerCards(card.Id) && !FinalCards(card.Id)).ToList(); + List UnTunrercards = Bot.GetMonstersInMainZone().Where(card => card != null && card.IsFaceup() && !TurnerCards(card.Id) && !FinalCards(card.Id)).ToList(); + if (UnTunrercards.Count == 0) return false; + else if (TunrerCards.Count >= UnTunrercards.Count && UnTunrercards.Count > 0) + { + foreach (var card in TunrerCards) + { + if (card == null) continue; + else if (materials.Count(ccard =>ccard != null && ccard.Id == card.Id) <= 0) materials.Add(card); + } + } + else + { + foreach (var card in UnTunrercards) + { + if (card == null) continue; + else if (materials.Count(ccard =>ccard != null && ccard.Id == card.Id) <= 0) materials.Add(card); + } + } + if (materials.Count <=1) return false; + AI.SelectMaterials(materials); + summon_Elf = true; + return true; + } + private bool ElfFunction() + { + if (Duel.Player == 0) + { + activate_Elf = true; + AI.SelectCard(CardId.Motorbike); + return Bot.HasInGraveyard(CardId.Motorbike); + } + List cards1 = GetZoneCards(CardLocation.MonsterZone, Enemy); + List cards2 = GetZoneCards(CardLocation.SpellZone, Enemy); + if (cards1.Count() > 0 || cards2.Count() >= 3) + { + if (Bot.HasInExtra(CardId.Unicorn) && Bot.HasInGraveyard(CardId.IP)) + AI.SelectCard(CardId.IP); + else + { + AI.SelectCard(CardId.Motorbike); + AI.SelectPosition(CardPosition.FaceUpDefence); + } + activate_Elf = true; + return Bot.HasInGraveyard(CardId.Motorbike) || Bot.HasInGraveyard(CardId.IP); + } + return false; + } + private bool RegulusFunction() + { + if (Card.Location == CardLocation.Hand) + { + int tributeId = -1; + if (Bot.HasInGraveyard(CardId.Soulpiercer)) + {tributeId = CardId.Soulpiercer;} + else if (Bot.HasInGraveyard(CardId.Motorbike)) + {tributeId = CardId.Motorbike;} + AI.SelectCard(tributeId); + activate_Genius = true; + return true; + } + else if (Duel.LastChainPlayer == 1) + { + return true; + } + return false; + } + private bool FleurFunction() + { + if (ActivateDescription == Util.GetStringId(CardId.Fleur, 0)) + { + ClientCard card = Util.GetProblematicEnemyMonster(0, true); + if (card != null) + { + AI.SelectCard(card); + return true; + } + card = Util.GetBestEnemySpell(true); + if (card != null) + { + AI.SelectCard(card); + return true; + } + List cards = GetZoneCards(CardLocation.Onfield, Enemy); + cards = cards.Where(tcard => tcard != null && !tcard.IsShouldNotBeTarget()).ToList(); + if (cards.Count <= 0) return false; + AI.SelectCard(cards); + return true; + } + else if (ActivateDescription == Util.GetStringId(CardId.Fleur, 1)) + { + return Duel.LastChainPlayer == 1; + } + return false; + } + private bool IPLinkFunction() + { + List materials = GetZoneCards(CardLocation.MonsterZone,Bot).Where(card => card != null && card.IsFaceup() && card.Id != CardId.Scarecrow && (card.Id != CardId.Elf || (card.Id == CardId.Elf && !summon_Elf)) && !FinalCards(card.Id)).ToList(); + if (materials.Count <=1) return false; + if (Bot.MonsterZone[6] != null && Bot.MonsterZone[6].Controller == 0 && Bot.MonsterZone[6].HasType(CardType.Link)) + { + if (Bot.MonsterZone[2] != null && FinalCards(Bot.MonsterZone[2].Id) && Bot.MonsterZone[4] != null && FinalCards(Bot.MonsterZone[4].Id)) + return false; + } + else if (Bot.MonsterZone[5] != null && Bot.MonsterZone[5].Controller == 0 && Bot.MonsterZone[5].HasType(CardType.Link)) + { + if (Bot.MonsterZone[2] != null && FinalCards(Bot.MonsterZone[2].Id) && Bot.MonsterZone[0] != null && FinalCards(Bot.MonsterZone[0].Id)) + return false; + } + AI.SelectMaterials(materials); + return true; + } + private bool PSYFunction() + { + activate_PSY = true; + return true; + } + private bool IPFunction() + { + if (Duel.LastChainPlayer == 0) return false; + if (Bot.HasInExtra(CardId.Unicorn)) + { + List material = new List(); + List cards = GetZoneCards(CardLocation.MonsterZone,Bot).Where(card => card != null && card != Card && card.IsFaceup() && !FinalCards(card.Id) && card.Id != CardId.IP && card.Id != CardId.Scarecrow).ToList(); + List Enemycards = GetZoneCards(CardLocation.MonsterZone,Enemy); + if (activate_Sarutobi) Enemycards = GetZoneCards(CardLocation.Onfield,Enemy); + if (Bot.Hand.Count == 0 || Enemycards.Count(card => card != null && !card.IsShouldNotBeTarget()) == 0 || cards.Count == 0) return false; + bool linkchk = false; + foreach (var card in cards) + { + if (card != null && (card.Id != CardId.Elf || (card.Id == CardId.Elf && !summon_Elf))) + { + material.Add(card); + linkchk = true; + break; + } + } + AI.SelectCard(CardId.Unicorn); + material.Insert(0,Card); + AI.SelectMaterials(material); + if (!to_deck) to_deck = true; + return linkchk; + } + return false; + } + private bool SarutobiFunction() + { + List Enemycards = GetZoneCards(CardLocation.SpellZone,Enemy); + if (Enemycards.Count(card => card != null && !card.IsShouldNotBeTarget()) == 0 || to_deck) return false; + AI.SelectCard(Enemycards); + activate_Sarutobi = true; + return true; + } + } +} diff --git a/Game/AI/Decks/SwordsoulExecutor.cs b/Game/AI/Decks/SwordsoulExecutor.cs index 64f75335..446e3070 100644 --- a/Game/AI/Decks/SwordsoulExecutor.cs +++ b/Game/AI/Decks/SwordsoulExecutor.cs @@ -1,2617 +1,3118 @@ -using System; +using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using System.Linq; -using YGOSharp.OCGWrapper.Enums; +using System; +using YGOSharp.OCGWrapper; -namespace WindBot.Game.AI.Decks { +namespace WindBot.Game.AI.Decks +{ [Deck("Swordsoul", "AI_Swordsoul")] - class SwordsoulExecutor : DefaultExecutor { - - #region Card IDs - - public class CardId { - // tenyi core - public const int TenyiAshuna = 87052196; - public const int TenyiVishuda = 23431858; - public const int TenyiAdhara = 98159737; - - // swordsoul core - public const int SwordsoulLongYuan = 93490856; - public const int SwordsoulTaia = 56495147; - public const int SwordsoulMoYe = 20001443; - - // handtraps + effect negates - public const int AshBlossomAndJoyousSpring = 14558127; - public const int InfiniteImpermanence = 10045474; - public const int ForbiddenChalice = 25789292; - //swordsoul support + public class SwordsoulExecutor : DefaultExecutor + { + public class CardId + { + public const int NibiruThePrimalBeing = 27204311; + public const int TenyiSpirit_Ashuna = 87052196; + public const int TenyiSpirit_Vishuda = 23431858; + public const int SwordsoulStrategistLongyuan = 93490856; + public const int SwordsoulOfTaia = 56495147; + public const int SwordsoulOfMoYe = 20001443; + public const int IncredibleEcclesiaTheVirtuous = 55273560; + // _CardId.AshBlossom = 14558127; + // _CardId.MaxxC = 23434538; + // _CardId.EffectVeiler = 97268402; + public const int TenyiSpirit_Adhara = 98159737; + + // _CardId.PotOfDesires = 35261759; public const int SwordsoulEmergence = 56465981; - public const int SwordsoulSummit = 93850690; - public const int SwordsoulBlackout = 14821890; - - public const int IncredibleEcclesia = 55273560; - public const int PotOfDesires = 35261759; - - // tenyi support - public const int VesselForDragonCycle = 65124425; + public const int SwordsoulSacredSummit = 93850690; + // _CardId.CalledByTheGrave = 24224830; + public const int CrossoutDesignator = 65681983; - // extra deck - public const int SwordsoulChengying = 96633955; - public const int SwordsoulChixiao = 69248256; + // _CardId.InfiniteImpermanence = 10045474; + public const int SwordsoulBlackout = 14821890; - public const int YangZingChaofeng = 19048328; - public const int YangZingBaxia = 83755611; - public const int YangZingYazi = 43202238; + public const int GeomathmechFinalSigma = 42632209; + public const int PsychicEndPunisher = 60465049; + public const int SwordsoulSupremeSovereign_Chengying = 96633955; + public const int BaronneDeFleur = 84815190; + public const int SwordsoulSinisterSovereign_QixingLongyuan = 47710198; + public const int AdamancipatorRisen_Dragite = 9464441; + public const int DracoBerserkerOfTheTenyi = 5041348; + public const int SwordsoulGrandmaster_Chixiao = 69248256; + public const int BaxiaBrightnessOfTheYangZing = 83755611; + public const int YaziEvilOfTheYangZing = 43202238; + public const int ShamanOfTheTenyi = 78917791; + public const int MonkOfTheTenyi = 32519092; - public const int BlackRoseDragon = 73580471; + public const int SwordsoulToken = 20001444; - public const int TenyiMonk = 32519092; - public const int TenyiDracoBeserker = 05041348; + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + } - public const int RuddyRoseDragon = 40139997; - public const int BaronneDeFluer = 84815190; - public const int AdamancipatorDragnite = 09464441; + public SwordsoulExecutor(GameAI ai, Duel duel) + : base(ai, duel) + { + // startup effect + AddExecutor(ExecutorType.Activate, CardId.BaxiaBrightnessOfTheYangZing, BaxiaBrightnessOfTheYangZingActivate); + AddExecutor(ExecutorType.Activate, CardId.YaziEvilOfTheYangZing, YaziEvilOfTheYangZingActivate); + AddExecutor(ExecutorType.Activate, CardId.IncredibleEcclesiaTheVirtuous, IncredibleEcclesiaTheVirtuousActivate); + + // quick effect + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, CardId.CrossoutDesignator, CrossoutDesignatorActivate); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, CardId.SwordsoulGrandmaster_Chixiao, SwordsoulGrandmaster_ChixiaoActivate); + AddExecutor(ExecutorType.Activate, _CardId.EffectVeiler, EffectVeilerActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, CardId.SwordsoulSinisterSovereign_QixingLongyuan, SwordsoulSinisterSovereign_QixingLongyuanActivate); + AddExecutor(ExecutorType.Activate, CardId.DracoBerserkerOfTheTenyi, DracoBerserkerOfTheTenyiActivate); + AddExecutor(ExecutorType.Activate, CardId.AdamancipatorRisen_Dragite, AdamancipatorRisen_DragiteActivate); + AddExecutor(ExecutorType.Activate, CardId.BaronneDeFleur, BaronneDeFleurActivate); + AddExecutor(ExecutorType.Activate, CardId.SwordsoulSupremeSovereign_Chengying, SwordsoulSupremeSovereign_ChengyingActivate); + + // free chain + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + AddExecutor(ExecutorType.Activate, CardId.NibiruThePrimalBeing, NibiruThePrimalBeingActivate); + + // startup effect + AddExecutor(ExecutorType.Activate, CardId.SwordsoulEmergence, SwordsoulEmergenceActivate); + AddExecutor(ExecutorType.Activate, CardId.SwordsoulBlackout, SwordsoulBlackoutActivate); + + // synchro + AddExecutor(ExecutorType.SpSummon, CardId.YaziEvilOfTheYangZing, YaziEvilOfTheYangZingSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.BaxiaBrightnessOfTheYangZing, BaxiaBrightnessOfTheYangZingSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.SwordsoulGrandmaster_Chixiao, SwordsoulGrandmaster_ChixiaoSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.AdamancipatorRisen_Dragite, AdamancipatorRisen_DragiteSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.DracoBerserkerOfTheTenyi, DracoBerserkerOfTheTenyiSpSummon); + + AddExecutor(ExecutorType.SpSummon, Level10SpSummonCheckInit); + AddExecutor(ExecutorType.SpSummon, Level10SpSummonCheckCount); + AddExecutor(ExecutorType.SpSummon, Level10SpSummonCheckDecide); + AddExecutor(ExecutorType.SpSummon, Level10SpSummonCheckFinal); + + // startup effect + AddExecutor(ExecutorType.Activate, CardId.SwordsoulOfMoYe, SwordsoulOfMoYeActivate); + AddExecutor(ExecutorType.Activate, CardId.SwordsoulOfTaia, SwordsoulOfTaiaActivate); + + // summon + AddExecutor(ExecutorType.Activate, TenyiForShamanSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.IncredibleEcclesiaTheVirtuous, IncredibleEcclesiaTheVirtuousSpSummon); + AddExecutor(ExecutorType.Summon, CardId.SwordsoulOfMoYe, SwordsoulOfMoYeSummon); + AddExecutor(ExecutorType.Summon, CardId.IncredibleEcclesiaTheVirtuous, IncredibleEcclesiaTheVirtuousSummon); + AddExecutor(ExecutorType.Summon, CardId.SwordsoulOfTaia, SwordsoulOfTaiaSummon); + + // activate + AddExecutor(ExecutorType.Activate, CardId.SwordsoulStrategistLongyuan, SwordsoulStrategistLongyuanActivate); + AddExecutor(ExecutorType.Activate, _CardId.PotOfDesires, PotOfDesiresActivate); + AddExecutor(ExecutorType.Activate, CardId.ShamanOfTheTenyi, ShamanOfTheTenyiActivate); + AddExecutor(ExecutorType.Activate, CardId.SwordsoulSacredSummit, SwordsoulSacredSummitActivate); + AddExecutor(ExecutorType.Activate, CardId.TenyiSpirit_Vishuda, TenyiSpirit_VishudaActivate); + AddExecutor(ExecutorType.Activate, CardId.TenyiSpirit_Ashuna, TenyiSpirit_AshunaActivate); + AddExecutor(ExecutorType.Activate, CardId.TenyiSpirit_Adhara, TenyiSpirit_AdharaActivate); + AddExecutor(ExecutorType.Activate, TenyiForBlackoutSpSummon); + + // other + AddExecutor(ExecutorType.SpSummon, CardId.GeomathmechFinalSigma, GeomathmechFinalSigmaSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.PsychicEndPunisher, PsychicEndPunisherSpSummon); + AddExecutor(ExecutorType.Summon, TunerForSynchroSummon); + AddExecutor(ExecutorType.Summon, WyrmForBlackoutSummon); + AddExecutor(ExecutorType.SpSummon, CardId.ShamanOfTheTenyi, ShamanOfTheTenyiSpSummon); + AddExecutor(ExecutorType.SpSummon, CardId.MonkOfTheTenyi, MonkOfTheTenyiSpSummon); + AddExecutor(ExecutorType.Repos, DefaultMonsterRepos); - // token - public const int SwordsoulToken = 20001444; + AddExecutor(ExecutorType.Activate, CardId.PsychicEndPunisher, PsychicEndPunisherActivate); + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); } - private readonly int[] Wyrms = - { - CardId.TenyiAshuna, - CardId.TenyiVishuda, - CardId.TenyiAdhara, - CardId.SwordsoulLongYuan, - CardId.SwordsoulTaia, - CardId.SwordsoulMoYe, - CardId.SwordsoulChengying, - CardId.SwordsoulChixiao, - CardId.YangZingChaofeng, - CardId.YangZingBaxia, - CardId.YangZingYazi, - CardId.TenyiMonk, - CardId.SwordsoulToken + const int SetcodeTimeLord = 0x4a; + const int SetcodeYangZing = 0x9e; + const int SetcodePhantom = 0xdb; + const int SetcodeOrcust = 0x11b; + const int SetcodeTenyi = 0x12c; + const int SetcodeSwordsoul = 0x16b; + const int SetcodeFloowandereeze = 0x16d; + List normalCounterList = new List + { + _CardId.AshBlossom, CardId.BaronneDeFleur, 27548199, 4280258, 53262004 }; - - private readonly int[] SwordSouls = - { - CardId.SwordsoulLongYuan, - CardId.SwordsoulTaia, - CardId.SwordsoulMoYe, - CardId.SwordsoulChengying, - CardId.SwordsoulChixiao, - CardId.SwordsoulEmergence, - CardId.SwordsoulSummit, - CardId.SwordsoulBlackout, - CardId.SwordsoulToken + List notToNegateIdList = new List{ + 58699500 }; - - private readonly int[] Tenyis = - { - CardId.TenyiAshuna, - CardId.TenyiVishuda, - CardId.TenyiAdhara, - CardId.TenyiMonk + const int hintTimingMainEnd = 0x4; + const int hintReplaceDestroy = 96; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.SwordsoulStrategistLongyuan, CardId.SwordsoulOfTaia, CardId.SwordsoulOfMoYe, CardId.IncredibleEcclesiaTheVirtuous, + _CardId.AshBlossom, _CardId.MaxxC, _CardId.EffectVeiler, CardId.SwordsoulEmergence, _CardId.InfiniteImpermanence }}, + {2, new List { CardId.TenyiSpirit_Ashuna, _CardId.PotOfDesires, _CardId.CalledByTheGrave, CardId.SwordsoulBlackout }}, + {1, new List { CardId.NibiruThePrimalBeing, CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Adhara, CardId.SwordsoulSacredSummit, + CardId.CrossoutDesignator }}, }; - #endregion Card IDs - - #region Activate Effect Flags + List currentNegatingIdList = new List(); + bool enemyActivateMaxxC = false; + bool enemyActivateLockBird = false; + bool enemyActivateInfiniteImpermanenceFromHand = false; + List infiniteImpermanenceList = new List(); - private enum ActivatedEffect { - None = 0x0, - First = 0x1, - Second = 0x2, - Third = 0x4 - } + bool summoned = false; + bool onlyWyrmSpSummon = false; + List activatedCardIdList = new List(); + List canSpSummonLevel10IdList = new List(); - private bool NormalSummonUsed = false; + List effectUsedBaronneDeFleurList = new List(); + List currentNegateMonsterList = new List(); - private ActivatedEffect MoYeActivated = ActivatedEffect.None; - private ActivatedEffect LongYuanActivated = ActivatedEffect.None; - private ActivatedEffect TaiaActivated = ActivatedEffect.None; - private ActivatedEffect ChixiaoActivated = ActivatedEffect.None; - private ActivatedEffect ChengyingActivated = ActivatedEffect.None; + /// + /// Shuffle List and return a random-order card list + /// + public List ShuffleCardList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + ClientCard tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } - private ActivatedEffect EcclesiaActivated = ActivatedEffect.None; + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false) + { + List floodagateList = Enemy.GetMonsters().Where(c => c?.Data != null && + c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (floodagateList.Count() > 0) + { + floodagateList.Sort(CardContainer.CompareCardAttack); + floodagateList.Reverse(); + return floodagateList[0]; + } - private ActivatedEffect AshunaActivated = ActivatedEffect.None; - private ActivatedEffect VishudaActivated = ActivatedEffect.None; - private ActivatedEffect AdharaActivated = ActivatedEffect.None; - private ActivatedEffect BaxiaActivated = ActivatedEffect.None; + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterDangerous() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (dangerList.Count() > 0) + { + dangerList.Sort(CardContainer.CompareCardAttack); + dangerList.Reverse(); + return dangerList[0]; + } - private ActivatedEffect EmergenceActivated = ActivatedEffect.None; - private ActivatedEffect SummitActivated = ActivatedEffect.None; + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && + c.IsMonsterInvincible() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (invincibleList.Count() > 0) + { + invincibleList.Sort(CardContainer.CompareCardAttack); + invincibleList.Reverse(); + return invincibleList[0]; + } - private ActivatedEffect VesselActivated = ActivatedEffect.None; + if (attack == 0) + attack = Util.GetBestAttack(Bot); + List betterList = Enemy.MonsterZone.GetMonsters() + .Where(card => card.GetDefensePower() >= attack && card.IsAttack() && (!canBeTarget || !card.IsShouldNotBeTarget())).ToList(); + if (betterList.Count() > 0) + { + betterList.Sort(CardContainer.CompareCardAttack); + betterList.Reverse(); + return betterList[0]; + } + return null; + } - private ActivatedEffect BaronneActivated = ActivatedEffect.None; + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreNormalSpell = false) + { + List resultList = new List(); - private LinkedList EffectChain = new LinkedList(); + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (floodagateList.Count() > 0) + { + floodagateList.Sort(CardContainer.CompareCardAttack); + floodagateList.Reverse(); + resultList.AddRange(floodagateList); + } + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count() > 0) + { + resultList.AddRange(ShuffleCardList(problemEnemySpellList)); + } - public override void OnNewTurn() { - NormalSummonUsed = false; + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (dangerList.Count() > 0 + && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) + { + dangerList.Sort(CardContainer.CompareCardAttack); + dangerList.Reverse(); + resultList.AddRange(dangerList); + } - MoYeActivated = ActivatedEffect.None; - LongYuanActivated = ActivatedEffect.None; - TaiaActivated = ActivatedEffect.None; + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (invincibleList.Count() > 0) + { + invincibleList.Sort(CardContainer.CompareCardAttack); + invincibleList.Reverse(); + resultList.AddRange(invincibleList); + } - ChixiaoActivated = ActivatedEffect.None; - ChengyingActivated = ActivatedEffect.None; + List enemyMonsters = Enemy.GetMonsters().ToList(); + if (enemyMonsters.Count() > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach(ClientCard target in enemyMonsters) + { + if (target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2) ) + { + if (!canBeTarget || !(target.IsShouldNotBeTarget() || target.IsShouldNotBeMonsterTarget())) + { + if (!resultList.Contains(target)) + { + resultList.Add(target); + } + } + } + } + } - EcclesiaActivated = ActivatedEffect.None; + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous)).ToList(); + if (spells.Count() > 0 && !ignoreNormalSpell) + { + resultList.AddRange(ShuffleCardList(spells)); + } - EmergenceActivated = ActivatedEffect.None; - SummitActivated = ActivatedEffect.None; + return resultList; + } + + public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false) + { + ClientCard card = GetProblematicEnemyMonster(0, canBeTarget); + if (card != null) + return card; - VesselActivated = ActivatedEffect.None; + card = Enemy.MonsterZone.GetHighestAttackMonster(canBeTarget); + if (card != null) + return card; - AshunaActivated = ActivatedEffect.None; - VishudaActivated = ActivatedEffect.None; - AdharaActivated = ActivatedEffect.None; + List monsters = Enemy.GetMonsters(); - BaxiaActivated = ActivatedEffect.None; + // after GetHighestAttackMonster, the left monsters must be face-down. + if (monsters.Count() > 0 && !onlyFaceup) + return ShuffleCardList(monsters)[0]; - BaronneActivated &= ActivatedEffect.Second; + return null; + } - EffectChain.Clear(); + public ClientCard GetBestEnemySpell(bool onlyFaceup = false, bool canBeTarget = false) + { + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count() > 0) + { + return ShuffleCardList(problemEnemySpellList)[0]; + } - // activation counts do not get reset between sessions so we can only activate the same card 9 times before the game prevents the AI from selecting the card - ResetActivatedCount(); - } + List spells = Enemy.GetSpells().Where(card => !(card.IsFaceup() && card.IsCode(_CardId.EvenlyMatched))).ToList(); - #endregion Activate Effect Flags + List faceUpList = spells.Where(ecard => ecard.IsFaceup() && + ecard.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous)).ToList(); + if (faceUpList.Count() > 0) + { + return ShuffleCardList(faceUpList)[0]; + } - public SwordsoulExecutor(GameAI ai, Duel duel) - : base(ai, duel) { - - // negates - AddExecutor(ExecutorType.Activate, CardId.SwordsoulChixiao, ActivateChixiaoNegate); - AddExecutor(ExecutorType.Activate, CardId.RuddyRoseDragon, ActivateRuddyRoseNegate); - AddExecutor(ExecutorType.Activate, CardId.TenyiDracoBeserker, DefaultDisableMonster); - AddExecutor(ExecutorType.Activate, CardId.AdamancipatorDragnite, ActivateDragniteNegate); - AddExecutor(ExecutorType.Activate, CardId.AshBlossomAndJoyousSpring, DefaultAshBlossomAndJoyousSpring); - AddExecutor(ExecutorType.Activate, CardId.InfiniteImpermanence, DefaultInfiniteImpermanence); - AddExecutor(ExecutorType.Activate, CardId.ForbiddenChalice, DefaultDisableMonster); - AddExecutor(ExecutorType.Activate, CardId.BaronneDeFluer, ActivateBaronneNegate); - - // triggers - AddExecutor(ExecutorType.Activate, CardId.SwordsoulChengying, ActivateChengyingEffects); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulChixiao, ActivateChixiaoSearch); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulEmergence, ActivateEmergencLevelDown); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulSummit, ActivateSummitLevelDown); - AddExecutor(ExecutorType.Activate, CardId.VesselForDragonCycle, ActivateVesselSearch); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulTaia, ActivateTaiaMill); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulLongYuan, ActivateLongyuanDamage); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulMoYe, ActivateMoYeDraw); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulBlackout, BlackoutActivateSummon); - AddExecutor(ExecutorType.Activate, CardId.YangZingYazi, ActivateYaziSearch); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulMoYe, ActivateMoYeSummon); - AddExecutor(ExecutorType.Activate, CardId.BaronneDeFluer, ActivateBaronneRevive); - AddExecutor(ExecutorType.Activate, CardId.YangZingChaofeng, ActivateChaofengSearchEffects); - - // removal activations - AddExecutor(ExecutorType.Activate, CardId.YangZingYazi, ActivateYaziDestruction); - AddExecutor(ExecutorType.Activate, CardId.RuddyRoseDragon, ActivateRuddyRoseBanish); - AddExecutor(ExecutorType.Activate, CardId.BlackRoseDragon, ActivateBlackroseDestroy); - AddExecutor(ExecutorType.Activate, CardId.BaronneDeFluer, ActivateBaronneDestroy); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulBlackout, BlackoutActivateDestroy); - AddExecutor(ExecutorType.Activate, CardId.TenyiVishuda, ActivateVishudaInGrave); - - // other activations - AddExecutor(ExecutorType.Activate, CardId.TenyiAshuna, SpecialSummonAshuna); - AddExecutor(ExecutorType.Activate, CardId.TenyiVishuda, SpecialSummonVishuda); - AddExecutor(ExecutorType.Activate, CardId.TenyiAdhara, SpecialSummonAdhara); - AddExecutor(ExecutorType.Activate, CardId.TenyiAdhara, ActivateGraveAdhara); - AddExecutor(ExecutorType.Activate, CardId.TenyiAshuna, ActivateAshunaInGrave); - AddExecutor(ExecutorType.Activate, CardId.IncredibleEcclesia, ActivateEcclesiaSearch); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulTaia, ActivateTaiaSummon); - AddExecutor(ExecutorType.Activate, CardId.YangZingBaxia, BaxiaActivatedRevive); - AddExecutor(ExecutorType.Activate, CardId.YangZingBaxia, BaxiaActivatedShuffle); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulLongYuan, ActivateLongYuanSummon); - - // special summons - AddExecutor(ExecutorType.SpSummon, CardId.IncredibleEcclesia, SpecialSummonEcclesia); - AddExecutor(ExecutorType.SpSummon, CardId.TenyiMonk, SpecialSummonMonk); - AddExecutor(ExecutorType.SpSummon, CardId.YangZingBaxia, SummonBaxia); - AddExecutor(ExecutorType.SpSummon, CardId.YangZingChaofeng, SummonChaofeng); - AddExecutor(ExecutorType.SpSummon, CardId.SwordsoulChixiao, SummonChixiao); - AddExecutor(ExecutorType.SpSummon, CardId.AdamancipatorDragnite, SummonDragnite); - AddExecutor(ExecutorType.SpSummon, CardId.SwordsoulChengying, SummonChengying); - AddExecutor(ExecutorType.SpSummon, CardId.BaronneDeFluer, SummonBaronne); - AddExecutor(ExecutorType.SpSummon, CardId.RuddyRoseDragon, SummonRuddyRose); - AddExecutor(ExecutorType.SpSummon, CardId.BlackRoseDragon, SummonBlackRose); - AddExecutor(ExecutorType.SpSummon, CardId.BlackRoseDragon, ShouldSummonBlackRose); - AddExecutor(ExecutorType.SpSummon, CardId.YangZingYazi, SummonYazi); - AddExecutor(ExecutorType.SpSummon, CardId.TenyiDracoBeserker, SummonDraco); - - // summons - AddExecutor(ExecutorType.Summon, CardId.IncredibleEcclesia, SummonEcclesia); - AddExecutor(ExecutorType.Summon, CardId.SwordsoulTaia, SummonTaia); - AddExecutor(ExecutorType.Summon, CardId.SwordsoulMoYe, SummonMoYe); - AddExecutor(ExecutorType.Summon, CardId.TenyiAdhara, SummonAdhara); - - // spell activations - AddExecutor(ExecutorType.Activate, CardId.SwordsoulEmergence, ActivateEmergenceSearch); - AddExecutor(ExecutorType.Activate, CardId.SwordsoulSummit, ActivateSummit); - AddExecutor(ExecutorType.Activate, CardId.PotOfDesires, ActivatePotOfDesires); - - // set traps - AddExecutor(ExecutorType.SpellSet, CardId.SwordsoulBlackout, SetBlackout); - AddExecutor(ExecutorType.SpellSet, CardId.InfiniteImpermanence, DefaultSpellSet); - AddExecutor(ExecutorType.SpellSet, CardId.ForbiddenChalice, DefaultSpellSet); - - // util stuff - AddExecutor(ExecutorType.Repos, DefaultMonsterRepos); - } + if (spells.Count() > 0 && !onlyFaceup) + { + return ShuffleCardList(spells)[0]; + } - public override bool OnSelectHand() { - return true; + return null; } - public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) { - ClientCard currentCard = GetCurrentSearchCardFromChain(); - if(currentCard == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); - - if(currentCard.IsCode(CardId.SwordsoulChixiao) && ChixiaoActivated.HasFlag(ActivatedEffect.First)) { - ClientCard selected = ChixiaoSearchSelection(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); - - return new List() { selected }; + public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false, bool checkGrave = false) + { + ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget); + if (card != null) + { + return card; } - if(currentCard.IsCode(CardId.SwordsoulTaia) && TaiaActivated.HasFlag(ActivatedEffect.Second)) { - ClientCard selected = TaiaMillSelection(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); - - return new List() { selected }; + card = GetBestEnemySpell(onlyFaceup, canBeTarget); + if (card != null) + { + return card; + } + + if (checkGrave && Enemy.Graveyard.Count() > 0) + { + List graveMonsterList = Enemy.Graveyard.GetMatchingCards(c => c.IsMonster()).ToList(); + if (graveMonsterList.Count() > 0) + { + graveMonsterList.Sort(CardContainer.CompareCardAttack); + graveMonsterList.Reverse(); + return graveMonsterList[0]; + } + return ShuffleCardList(Enemy.Graveyard.ToList())[0]; } - if(currentCard.IsCode(CardId.IncredibleEcclesia) && EcclesiaActivated.HasFlag(ActivatedEffect.Second)) { - ClientCard selected = EcclesiaSearchSelection(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); + return null; + } - return new List() { selected }; - } + public List GetNormalEnemyTargetList(bool canBeTarget = true) + { + List targetList = GetProblematicEnemyCardList(canBeTarget); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card)).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleCardList(Enemy.GetSpells())); + targetList.AddRange(ShuffleCardList(Enemy.GetMonsters().Where(card => card.IsFacedown()).ToList())); - if(currentCard.IsCode(CardId.TenyiAshuna) && AshunaActivated.HasFlag(ActivatedEffect.Second)) { - ClientCard selected = AshunaSearchSelection(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); + return targetList; + } - return new List() { selected }; + public List GetMonsterListForTargetNegate(bool canBeMonsterTarget = false, bool canBeTrapTarget = false) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; } - if(currentCard.IsCode(CardId.VesselForDragonCycle) && VesselActivated.HasFlag(ActivatedEffect.First)) { - ClientCard selected = VesselMillSelection(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); + // negate before used + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && (!canBeMonsterTarget || !card.IsShouldNotBeMonsterTarget()) && (!canBeTrapTarget || !card.IsShouldNotBeSpellTrapTarget()) + && !currentNegateMonsterList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } - return new List() { selected }; + // negate monster effect on the field + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && (!canBeMonsterTarget || !chainingCard.IsShouldNotBeMonsterTarget()) && (!canBeTrapTarget || !chainingCard.IsShouldNotBeSpellTrapTarget()) + && !chainingCard.IsShouldNotBeTarget() && !currentNegateMonsterList.Contains(chainingCard)) + { + resultList.Add(chainingCard); + } } - if(currentCard.IsCode(CardId.VesselForDragonCycle) && VesselActivated.HasFlag(ActivatedEffect.Second)) { - ClientCard selected = VesselSearchSelection(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); + return resultList; + } - return new List() { selected }; + /// + /// Check whether negate opposite's effect and clear flag + /// + public void CheckDeactiveFlag() + { + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && Duel.LastChainPlayer == 1) + { + if (lastChainCard.Controller == 1 && lastChainCard.Location == CardLocation.MonsterZone) + { + currentNegateMonsterList.Add(lastChainCard); + } } + } - if(currentCard.IsCode(CardId.SwordsoulChengying)) { - List selected = SelectChengyingTargets(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); + /// + /// Check negated turn count of id + /// + public int CheckCalledbytheGrave(int id) + { + if (currentNegatingIdList.Contains(id)) return 1; + if (DefaultCheckWhetherCardIdIsNegated(id)) return 1; + return 0; + } - return selected; + /// + /// Check remain cards in deck + /// + /// Card's ID + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) { + return Bot.GetRemainingCount(id, count); + } } + return 0; + } - if(currentCard.IsCode(CardId.YangZingYazi)) { - ClientCard selected = YaziSearchSelection(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); + /// + /// Whether spell or trap will be negate. If so, return true. + /// + /// is counter trap + /// check target + /// + public bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + // target default set + if (target == null) target = Card; + // won't negate if not on field + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + // negate judge + if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap()) + { + if (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true)) return true; + } + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) { + return true; + } + } + // how to get here? + return false; + } - return new List() { selected }; + /// + /// Check whether'll be negated + /// + /// check whether card itself is disabled. + public bool CheckWhetherNegated() + { + if ((Card.IsSpell() || Card.IsTrap()) && CheckSpellWillBeNegate()){ + return true; } + if (DefaultCheckWhetherCardIsNegated(Card)) { + return true; + } + if (Card.IsMonster() && Card.Location == CardLocation.MonsterZone && Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card)) || Bot.MonsterZone.Any(card => CheckNumber41(card))) + { + return true; + } + } + return false; + } - if(currentCard.IsCode(CardId.YangZingChaofeng)) { - ClientCard selected = SelectChaofengTarget(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); + public bool CheckNumber41(ClientCard card) + { + return card != null && card.IsFaceup() && card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled(); + } - return new List() { selected }; + /// + /// Check whether bot is at advantage. + /// + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && + (Bot.GetMonsters().Any(card => card.IsFaceup()) || (Duel.Player == 0 && Duel.Turn == 1))) + { + return true; } + return false; + } - if(currentCard.IsCode(CardId.BaronneDeFluer)) { - ClientCard selected = BaronneDestroyTarget(cards); - if(selected == null) - return base.OnSelectCard(cards, min, max, hint, cancelable); - - return new List() { selected }; + /// + /// Check whether last chain card should be disabled. + /// + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (notToNegateIdList.Contains(lastcard.Id)) return false; + if (DefaultCheckWhetherCardIsNegated(lastcard)) return false; + if (Duel.CurrentChain.Count >= 2) + { + ClientCard lastlastChainCard = Duel.CurrentChain[Duel.CurrentChain.Count - 2]; + ClientCard lastChainCard = Duel.CurrentChain[Duel.CurrentChain.Count - 1]; + if (lastlastChainCard?.Controller == 0 && lastChainCard?.Controller == 1 && lastChainCard.IsCode(normalCounterList)) + { + bool notImportantFlag = lastlastChainCard.Location == CardLocation.Grave + && (lastlastChainCard.IsCode(CardId.SwordsoulOfTaia) || lastlastChainCard.IsCode(CardId.SwordsoulOfMoYe) || lastlastChainCard.IsCode(CardId.SwordsoulStrategistLongyuan)); + notImportantFlag |= lastlastChainCard.IsCode(CardId.PsychicEndPunisher) && Bot.LifePoints < Enemy.LifePoints; + if (notImportantFlag) + { + return false; + } + } } - return base.OnSelectCard(cards, min, max, hint, cancelable); + return true; } + /// + /// check enemy's dangerous card in grave + /// + public List CheckDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantom))).ToList(); + List dangerMonsterIdList = new List{ + 99937011, 63542003, CardId.TenyiSpirit_Adhara, CardId.TenyiSpirit_Ashuna, CardId.TenyiSpirit_Vishuda, + 9411399, 28954097, 30680659 + }; + return result; + } - private bool ActivatePotOfDesires() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; + /// + /// go first + /// + public override bool OnSelectHand() + { + return true; + } - if(Card.IsDisabled()) - return false; + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + // Geomathmech Final Sigma always place on extra monster zone + if (cardId == CardId.GeomathmechFinalSigma && location == CardLocation.MonsterZone) + { + if ((Zones.z5 & available) > 0) return Zones.z5; + if ((Zones.z6 & available) > 0) return Zones.z6; + } + return base.OnSelectPlace(cardId, player, location, available); + } - // on turn one we want to avoid breaking deck combos if we have plays - if(Duel.Turn == 1) { - if(ShouldActivateGraveAshuna()) - return false; + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId); + if (cardData != null) + { + if (cardData.Id == CardId.PsychicEndPunisher) + { + return CardPosition.FaceUpAttack; + } + if (Util.IsTurn1OrMain2()) + { + bool turnDefense = false; + if (!cardData.HasType(CardType.Synchro) || cardData.Attack <= cardData.Defense) + { + turnDefense = true; + } + if (turnDefense) + { + return CardPosition.FaceUpDefence; + } + } + if (Duel.Player == 1) + { + if (!cardData.HasType(CardType.Synchro) || cardData.Defense >= cardData.Attack || Util.IsOneEnemyBetterThanValue(cardData.Attack, true)) + { + return CardPosition.FaceUpDefence; + } + } else if (cardData.HasType(CardType.Synchro)) + { + return CardPosition.FaceUpAttack; + } + int bestBotAttack = Math.Max(Util.GetBestAttack(Bot), cardData.Attack); + if (Util.IsAllEnemyBetterThanValue(bestBotAttack, true)) + { + return CardPosition.FaceUpDefence; + } + } + return base.OnSelectPosition(cardId, positions); + } - if(ShouldSpecialSummonEcclesia()) - return false; + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + if (Util.ChainContainPlayer(1) && hint == HintMsg.Remove && Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + int botCount = Bot.GetMonsterCount() + Bot.GetSpellCount(); + int oppositeCount = Enemy.GetMonsterCount() + Enemy.GetSpellCount(); + if (botCount - oppositeCount == min && min == max) + { + Logger.DebugWriteLine("=== Evenly Matched activated."); + List banishList = new List(); + List botMonsters = Bot.GetMonsters().Where(card => !card.HasType(CardType.Token)).ToList(); + + // non-synchro monster + List faceDownMonsters = botMonsters.Where(card => card.IsFacedown()).ToList(); + banishList.AddRange(faceDownMonsters); + List nonSynchroMonsters = botMonsters.Where(card => !card.HasType(CardType.Synchro) && !banishList.Contains(card)).ToList(); + nonSynchroMonsters.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(nonSynchroMonsters); + + // spells + List spells = Bot.GetSpells(); + banishList.AddRange(ShuffleCardList(spells)); + + // synchro monster + List synchroMonsters = botMonsters.Where(card => card.HasType(CardType.Synchro) && !banishList.Contains(card)).ToList(); + synchroMonsters.Sort(CardContainer.CompareCardAttack); + banishList.AddRange(synchroMonsters); + + return Util.CheckSelectCount(banishList, cards, min, max); + } + } - if(CanActivateTaiaFromField()) - return false; + return base.OnSelectCard(cards, min, max, hint, cancelable); + } - if(ShouldSummonChixiao()) - return false; + public override void OnNewTurn() + { + enemyActivateMaxxC = false; + enemyActivateLockBird = false; - if(ShouldSummonYazi()) - return false; + infiniteImpermanenceList.Clear(); - if(ShouldSummonBaronne()) - return false; + summoned = false; + onlyWyrmSpSummon = false; + enemyActivateInfiniteImpermanenceFromHand = false; + activatedCardIdList.Clear(); + currentNegateMonsterList.Clear(); + currentNegatingIdList.Clear(); + base.OnNewTurn(); + } - if(ShouldSummonBlackRose()) - return false; + public override void OnChainSolved(int chainIndex) + { + ChainInfo currentCard = Duel.GetCurrentSolvingChainInfo(); + if (currentCard != null && !Duel.IsCurrentSolvingChainNegated() && currentCard.ActivatePlayer == 1) + { + if (currentCard.IsCode(_CardId.MaxxC)) + enemyActivateMaxxC = true; + if (currentCard.IsCode(_CardId.LockBird)) + enemyActivateLockBird = true; + if (currentCard.IsCode(_CardId.InfiniteImpermanence) && !enemyActivateInfiniteImpermanenceFromHand) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == currentCard.RelatedCard) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + } + } - if(ShouldSummonDragnite()) - return false; + public override void OnChainEnd() + { + currentNegateMonsterList.Clear(); + currentNegatingIdList.Clear(); + for (int idx = effectUsedBaronneDeFleurList.Count() - 1; idx >= 0; -- idx) + { + ClientCard checkTarget = effectUsedBaronneDeFleurList[idx]; + if (checkTarget == null || checkTarget.IsFacedown() || checkTarget.Location != CardLocation.MonsterZone) + { + effectUsedBaronneDeFleurList.RemoveAt(idx); + } + } + enemyActivateInfiniteImpermanenceFromHand = false; + base.OnChainEnd(); + } - if(CanActivateYaziDestruction()) - return false; + public override void OnMove(ClientCard card, int previousControler, int previousLocation, int currentControler, int currentLocation) + { + if (previousControler == 1) + { + if (card != null) + { + if (card.IsCode(_CardId.InfiniteImpermanence) && previousLocation == (int)CardLocation.Hand && currentLocation == (int)CardLocation.SpellZone) + enemyActivateInfiniteImpermanenceFromHand = true; + } } - bool hasTenyiExtendersInHand = Bot.HasInHand(CardId.TenyiAdhara) || Bot.HasInHand(CardId.TenyiVishuda); - bool hasAnyTenyiInHand = Bot.HasInHand(CardId.TenyiAshuna) || hasTenyiExtendersInHand; + base.OnMove(card, previousControler, previousLocation, currentControler, currentLocation); + } - if(Bot.HasInHand(CardId.TenyiAshuna) && hasTenyiExtendersInHand) - return false; + /// + /// Select spell/trap's place randomly to avoid InfiniteImpermanence and so on. + /// + /// Card to set(default current card) + /// Whether need to avoid InfiniteImpermanence + /// Whether need to avoid set in this place + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } - if(Bot.HasInHand(CardId.VesselForDragonCycle) && hasAnyTenyiInHand) + public bool NibiruThePrimalBeingActivate() + { + if (CheckWhetherNegated()) return false; + if (Duel.Player == 0 || Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasType(CardType.Synchro))) + { return false; + } + + if (Util.GetBestAttack(Enemy) > Util.GetBestAttack(Bot)) + { + // end main phase + if ((CurrentTiming & hintTimingMainEnd) != 0) + { + SelectNibiruPosition(); + return true; + } + + // avoid Baronne de Fleur + List tunerList = Enemy.GetMonsters().Where(card => card.IsFaceup() && card.IsTuner() && !card.HasType(CardType.Xyz | CardType.Link)).ToList(); + List nonTunerList = Enemy.GetMonsters().Where(card => card.IsFaceup() && !card.IsTuner() && !card.HasType(CardType.Xyz | CardType.Link)).ToList(); + foreach (ClientCard tuner in tunerList) + { + foreach (ClientCard nonTuner in nonTunerList) + { + if (tuner.Level + nonTuner.Level == 10) + { + SelectNibiruPosition(); + return true; + } + } + } + } - return DefaultPotOfDesires(); + return false; } - #region Vessel Code - - private bool ActivateVesselSearch() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(Bot.GetMonsters().Count == 0) - return false; + public void SelectNibiruPosition() + { + int totalAttack = Bot.GetMonsters().Where(card => card.IsFaceup()).Sum(m => (int?)m.Attack) ?? 0; + totalAttack += Enemy.GetMonsters().Where(card => card.IsFaceup()).Sum(m => (int?)m.Attack) ?? 0; + Logger.DebugWriteLine("Nibiru token attack: " + totalAttack.ToString()); + if (totalAttack >= 3000) + { + AI.SelectPosition(CardPosition.FaceUpDefence); + AI.SelectPosition(CardPosition.FaceUpDefence); + } else { + AI.SelectPosition(CardPosition.FaceUpAttack); + AI.SelectPosition(CardPosition.FaceUpAttack); + } + } - if(Bot.MonsterZone.GetMatchingCardsCount(card => !card.HasType(CardType.Effect)) == 0) - return false; + public bool TenyiSpirit_AshunaActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.TenyiSpirit_Ashuna, 0)) + { + // special summon + if (TenyiSpSummonForTaiaCheck() || Level7TenyiSpSummonCheck()) + { + return true; + } - if(ShouldSpecialSummonEcclesia()) - return false; + } else if (ActivateDescription == Util.GetStringId(CardId.TenyiSpirit_Ashuna, 1) && Card.Location == CardLocation.Grave && CheckCalledbytheGrave(Card.Id) == 0) + { + // deck summon + + // trigger blackout + if (Bot.HasInHandOrInSpellZone(CardId.SwordsoulBlackout)) + { + List blackoutTarget = Bot.GetMonsters().Where(card => card.IsFaceup() + && !card.HasType(CardType.Synchro) && card.HasRace(CardRace.Wyrm)).ToList(); + if (blackoutTarget.Count() == 0) + { + AI.SelectCard(CardId.TenyiSpirit_Adhara, CardId.TenyiSpirit_Vishuda); + onlyWyrmSpSummon = true; + activatedCardIdList.Add(Card.Id); + return true; + } + } - if(CanActivateTaiaFromField()) - return false; + // for level8/10 synchro + List tunerIdList = new List{_CardId.AshBlossom, _CardId.EffectVeiler, CardId.TenyiSpirit_Adhara}; + bool hasTuner = Bot.GetMonsters().Any(card => card.IsFaceup() && card.IsCode(tunerIdList)); + hasTuner |= !summoned && Bot.HasInHand(tunerIdList); + if (hasTuner && CheckRemainInDeck(CardId.TenyiSpirit_Vishuda) > 0) + { + AI.SelectCard(CardId.TenyiSpirit_Ashuna); + onlyWyrmSpSummon = true; + activatedCardIdList.Add(Card.Id); + return true; + } + if (Bot.HasInMonstersZone(CardId.TenyiSpirit_Ashuna, false, false, true) && CheckRemainInDeck(CardId.TenyiSpirit_Adhara) > 0) + { + AI.SelectCard(CardId.TenyiSpirit_Adhara); + onlyWyrmSpSummon = true; + activatedCardIdList.Add(Card.Id); + return true; + } + } + return false; + } - if(ShouldSummonChixiao()) - return false; + public bool TenyiSpirit_VishudaActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.TenyiSpirit_Vishuda, 0)) + { + // special summon + if (TenyiSpSummonForTaiaCheck() || Level7TenyiSpSummonCheck()) + { + return true; + } - if(ShouldSummonYazi()) - return false; + } else if (ActivateDescription == Util.GetStringId(CardId.TenyiSpirit_Vishuda, 1) && Card.Location == CardLocation.Grave && CheckCalledbytheGrave(Card.Id) == 0) + { + // bounce + List dangerList = GetProblematicEnemyCardList(true, true); + if (dangerList.Count() > 0) + { + AI.SelectCard(dangerList); + return true; + } + } + return false; + } - if(ShouldSummonBaronne()) - return false; - - if(CanActivateYaziDestruction()) - return false; + public bool Level7TenyiSpSummonCheck() + { + List advanceSummonCheckList = new List{CardId.SwordsoulOfMoYe, CardId.SwordsoulOfTaia, CardId.IncredibleEcclesiaTheVirtuous}; + List tunerList = new List{CardId.TenyiSpirit_Adhara, _CardId.EffectVeiler, _CardId.AshBlossom}; + if (!summoned && !Bot.HasInHand(advanceSummonCheckList) && Bot.HasInHand(tunerList)) + { + return true; + } + if (Bot.HasInExtra(CardId.PsychicEndPunisher) && Bot.HasInMonstersZone(CardId.SwordsoulToken) && !onlyWyrmSpSummon) + { + return true; + } - VesselActivated |= ActivatedEffect.First; - return true; + return false; } - private ClientCard VesselMillSelection(IList cards) { - VesselActivated |= ActivatedEffect.Second; - - bool shouldSearch = true; - - // Make sure there are materials for ashuna in the deck while we are searching for mill - if(Bot.Hand.ContainsCardWithId(CardId.TenyiVishuda) && cards.GetMatchingCardsCount(card => card.IsCode(CardId.TenyiAdhara)) == 1) - shouldSearch = false; - - if(Bot.Hand.ContainsCardWithId(CardId.TenyiAdhara) && cards.GetMatchingCardsCount(card => card.IsCode(CardId.TenyiVishuda)) == 1) - shouldSearch = false; - - AI.SelectYesNo(shouldSearch); - - if(!Bot.HasInGraveyard(CardId.TenyiAshuna) && cards.ContainsCardWithId(CardId.TenyiAshuna)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAshuna)); - - if(cards.GetMatchingCardsCount(card => card.IsCode(CardId.TenyiVishuda)) > 1) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiVishuda)); - - if(cards.GetMatchingCardsCount(card => card.IsCode(CardId.TenyiAdhara)) > 1) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAdhara)); - - if(cards.ContainsCardWithId(CardId.SwordsoulLongYuan)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulLongYuan)); - - if(cards.ContainsCardWithId(CardId.SwordsoulMoYe)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulMoYe)); - - if(cards.ContainsCardWithId(CardId.SwordsoulTaia)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulTaia)); + public bool SwordsoulStrategistLongyuanActivate() + { + // damage effect + if (Card.Location != CardLocation.Hand) + { + return true; + } - return null; - } + // special summon token + if (CheckWhetherNegated() || (CheckAtAdvantage() && enemyActivateMaxxC && Util.IsTurn1OrMain2())) + { + return false; + } + List discardIdList = new List(); - private ClientCard VesselSearchSelection(IList cards) { - if(!VishudaActivated.HasFlag(ActivatedEffect.First) && cards.GetMatchingCardsCount(card => card.IsCode(CardId.TenyiVishuda)) > 1) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiVishuda)); + // discard Taia to SS + if (CheckAtAdvantage()) + { + if (Bot.HasInHand(CardId.SwordsoulSacredSummit) && Bot.HasInHand(CardId.SwordsoulOfTaia) + && !activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && !activatedCardIdList.Contains(CardId.SwordsoulSacredSummit)) + { + discardIdList.Add(CardId.SwordsoulOfTaia); + } + } - if(!AdharaActivated.HasFlag(ActivatedEffect.First) && cards.GetMatchingCardsCount(card => card.IsCode(CardId.TenyiAdhara)) > 1) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAdhara)); + // discard tenyi + if (discardIdList.Count() == 0) + { + List tenyiList = new List{CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Ashuna, CardId.TenyiSpirit_Adhara}; + foreach (int tenyiId in tenyiList) + { + if (Bot.HasInHand(tenyiId)) + { + discardIdList.Add(tenyiId); + } + } + } - if(cards.ContainsCardWithId(CardId.TenyiVishuda)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiVishuda)); + // discard dump card + if (discardIdList.Count() == 0) + { + List checkIdList = new List{ + CardId.SwordsoulOfTaia, CardId.SwordsoulOfMoYe, CardId.SwordsoulBlackout, CardId.SwordsoulStrategistLongyuan, CardId.SwordsoulEmergence + }; + foreach (int checkId in checkIdList) + { + if (Bot.Hand.Count(card => card != Card && card.IsCode(checkIdList)) > 1) + { + discardIdList.Add(checkId); + } + } + } - if(cards.ContainsCardWithId(CardId.TenyiAdhara)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAdhara)); + // discard card + if (discardIdList.Count() == 0) + { + List checkIdList = new List{ + CardId.SwordsoulOfTaia, CardId.SwordsoulOfMoYe, CardId.SwordsoulBlackout, CardId.SwordsoulStrategistLongyuan, + CardId.SwordsoulSacredSummit, CardId.SwordsoulEmergence + }; + foreach (int checkId in checkIdList) + { + if (Bot.Hand.Count(card => card != Card && card.IsCode(checkIdList)) >= 1) + { + discardIdList.Add(checkId); + } + } + } - if(cards.ContainsCardWithId(CardId.TenyiAshuna)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAshuna)); + if (discardIdList.Count() > 0) + { + AI.SelectCard(discardIdList); + AI.SelectPosition(CardPosition.FaceUpAttack); + AI.SelectPosition(CardPosition.FaceUpDefence); + activatedCardIdList.Add(Card.Id); + return true; + } - return null; + return false; } - #endregion Vessel Code + public bool SwordsoulOfTaiaActivate() + { + // send to GY effect + if (Card.Location != CardLocation.MonsterZone) + { + List sendToGYTarget = new List(); + + // send Mo Ye to SS + if (!Bot.HasInGraveyard(CardId.SwordsoulOfMoYe) && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0) + { + bool sendMoYe = false; + // baxia + if (Bot.HasInMonstersZone(CardId.BaxiaBrightnessOfTheYangZing, true, false, true + && !activatedCardIdList.Contains(CardId.BaxiaBrightnessOfTheYangZing))) + { + sendMoYe = true; + + } + if (Bot.HasInHand(CardId.SwordsoulSacredSummit) && !activatedCardIdList.Contains(CardId.SwordsoulSacredSummit)) { + if (Bot.Hand.Any(card => card.Id != CardId.SwordsoulSacredSummit && (card.HasSetcode(SetcodeSwordsoul) || card.HasRace(CardRace.Wyrm)))) + { + sendMoYe = true; + } + } - #region Ashuna Code + if (sendMoYe) + { + sendToGYTarget.Add(CardId.SwordsoulOfMoYe); + } + } + + // send Tenyi + List checkTenyiList = new List {CardId.TenyiSpirit_Adhara, CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Ashuna}; + foreach (int id in checkTenyiList) + { + if (CheckRemainInDeck(id) > 0) + { + sendToGYTarget.Add(id); + } + } - private bool SpecialSummonAshuna() { - if(!Card.Location.HasFlag(CardLocation.Hand)) + if (sendToGYTarget.Count() > 0) + { + AI.SelectCard(sendToGYTarget); + return true; + } return false; + } + + // special summon token + // ignore negate check if blackout in GY + if (Bot.HasInGraveyard(CardId.SwordsoulBlackout) && !activatedCardIdList.Contains(CardId.SwordsoulBlackout)) + { + AI.SelectCard(CardId.SwordsoulBlackout); + AI.SelectPosition(CardPosition.FaceUpDefence); + activatedCardIdList.Add(Card.Id); + return true; + } - if(HasEffectMonster()) + if (CheckWhetherNegated()) + { return false; + } + List banishIdList = new List(); - if(AshunaActivated.HasFlag(ActivatedEffect.First)) - return false; + List checkIdList = new List{ + CardId.SwordsoulStrategistLongyuan, CardId.SwordsoulEmergence, CardId.SwordsoulOfTaia, CardId.SwordsoulOfMoYe, CardId.MonkOfTheTenyi, + CardId.ShamanOfTheTenyi, CardId.SwordsoulSacredSummit, CardId.SwordsoulGrandmaster_Chixiao, CardId.TenyiSpirit_Ashuna, CardId.TenyiSpirit_Vishuda, + CardId.SwordsoulSinisterSovereign_QixingLongyuan, CardId.SwordsoulSupremeSovereign_Chengying, CardId.DracoBerserkerOfTheTenyi, + CardId.TenyiSpirit_Adhara + }; - // Do the combo no matter what if we have two tenyi - bool usableVishuna = Bot.Hand.ContainsCardWithId(CardId.TenyiVishuda); + // dump check + foreach (int checkId in checkIdList) + { + if (Bot.Graveyard.Count(card => card.IsCode(checkId)) > 1) + { + banishIdList.Add(checkId); + } + } - bool usableAdhara = Bot.Hand.ContainsCardWithId(CardId.TenyiAdhara) || Bot.Hand.ContainsCardWithId(CardId.VesselForDragonCycle); - if(usableVishuna || usableAdhara) { - AshunaActivated |= ActivatedEffect.First; - return true; + // priority check + if (banishIdList.Count() == 0) + { + foreach (int checkId in checkIdList) + { + if (Bot.HasInGraveyard(checkId)) + { + banishIdList.Add(checkId); + } + } } - // Moye combo is better - if(Bot.Hand.ContainsCardWithId(CardId.SwordsoulMoYe) && SoulswordMaterialCountInHand() <= 2) - return false; + if (banishIdList.Count() > 0) + { + AI.SelectCard(banishIdList); + AI.SelectPosition(CardPosition.FaceUpDefence); + activatedCardIdList.Add(Card.Id); + return true; + } - // Longyuan combo is better - if(Bot.Hand.ContainsCardWithId(CardId.SwordsoulLongYuan) && SoulswordMaterialCountInHand() <= 2) + return false; + } + + public bool SwordsoulOfTaiaSummon() + { + if (Bot.HasInGraveyard(CardId.SwordsoulBlackout)) + { + if (!activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && !activatedCardIdList.Contains(CardId.SwordsoulBlackout)) + { + summoned = true; + return true; + } + } + if (SummonLevel4ForSynchro()) + { + summoned = true; + return true; + } + if (CheckWhetherNegated()) + { return false; + } + if (SwordsoulOfTaiaEffectCheck() && !activatedCardIdList.Contains(CardId.SwordsoulOfTaia)) + { + summoned = true; + return true; + } - // Ecclesia combo is better - if(Bot.Hand.ContainsCardWithId(CardId.IncredibleEcclesia)) - return false; + return false; + } - AshunaActivated |= ActivatedEffect.First; - return true; + public bool SwordsoulOfTaiaEffectCheck(ClientCard exceptTarget = null) + { + if (exceptTarget == null) + { + exceptTarget = Card; + } + return Bot.Graveyard.Count(card => card != exceptTarget && (card.HasSetcode(SetcodeSwordsoul) || card.HasRace(CardRace.Wyrm))) > 0; } - private bool ShouldActivateGraveAshuna() { - if(!Bot.Graveyard.ContainsCardWithId(CardId.TenyiAshuna)) - return false; + public bool SwordsoulOfMoYeActivate() + { + // draw effect + if (Card.Location != CardLocation.MonsterZone) + { + return true; + } - if(!HasNonEffectMonster()) + // special summon token + if (CheckWhetherNegated()) + { return false; + } + List revealList = Bot.Hand.Where(card => card.HasSetcode(SetcodeSwordsoul) || card.HasRace(CardRace.Wyrm)).ToList(); + if (revealList.Count() > 0) + { + revealList = ShuffleCardList(revealList); + AI.SelectCard(revealList); + AI.SelectPosition(CardPosition.FaceUpDefence); + activatedCardIdList.Add(Card.Id); + return true; + } + return false; + } - if(Bot.HasInMonstersZone(CardId.TenyiAshuna)) + public bool SwordsoulOfMoYeSummon() + { + if (SummonLevel4ForSynchro()) + { + summoned = true; + return true; + } + if (CheckWhetherNegated()) + { return false; + } + if (SwordsoulOfMoYeEffectCheck() && !activatedCardIdList.Contains(CardId.SwordsoulOfMoYe)) + { + summoned = true; + return true; + } - if(AshunaActivated.HasFlag(ActivatedEffect.Second)) - return false; + return false; + } - if(CanActivateTaiaFromField()) - return false; + public bool SwordsoulOfMoYeEffectCheck(List exceptList = null) + { + if (exceptList == null) + { + exceptList = new List{Card}; + } + return Bot.Hand.Count(card => !exceptList.Contains(card) && (card.HasSetcode(SetcodeSwordsoul) || card.HasRace(CardRace.Wyrm))) > 0; + } - if(ShouldSummonYazi()) + public bool SummonLevel4ForSynchro() + { + bool hasNonTuner = Bot.GetMonsters().Any(card => card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link) && !card.IsTuner()); + if (hasNonTuner) + { return false; + } + List tunerList = Bot.GetMonsters().Where(card => + card.IsFaceup() && !card.HasType(CardType.Xyz | CardType.Link) && card.IsTuner()).ToList(); + if (tunerList.Count() > 0) + { + foreach (ClientCard tuner in tunerList) + { + int checkLevel = tuner.Level + 4; + if (Bot.ExtraDeck.Any(card => card.HasType(CardType.Synchro) && card.Level == checkLevel)) + { + return true; + } + } + } - if(ShouldSummonChixiao()) - return false; + return false; + } - if(CanActivateYaziDestruction()) - return false; + public bool IncredibleEcclesiaTheVirtuousActivate() + { + if (Card.Location == CardLocation.Grave) + { + return true; + } + if (Duel.Player == 0 && !CheckWhetherNegated()) + { + bool canActivateMoye = !activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0 + && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0 && SwordsoulOfMoYeEffectCheck(); + bool canActivateTaia = !activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && CheckRemainInDeck(CardId.SwordsoulOfTaia) > 0 + && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0 && SwordsoulOfTaiaEffectCheck(); + if (canActivateMoye && !summoned && !Bot.HasInHand(CardId.SwordsoulOfMoYe)) + { + AI.SelectCard(CardId.SwordsoulOfMoYe); + activatedCardIdList.Add(Card.Id); + return true; + } + if (canActivateTaia && !summoned && !Bot.HasInHand(CardId.SwordsoulOfTaia)) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + activatedCardIdList.Add(Card.Id); + return true; + } + if (canActivateMoye) + { + AI.SelectCard(CardId.SwordsoulOfMoYe); + activatedCardIdList.Add(Card.Id); + return true; + } + if (canActivateTaia) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + activatedCardIdList.Add(Card.Id); + return true; + } + } - if(ShouldSummonDragnite()) - return false; + return false; + } - if(ShouldSummonBlackRose()) + public bool IncredibleEcclesiaTheVirtuousSummon() + { + if (CheckWhetherNegated()) + { return false; - - // if we have a tenyi summoned then trigger for the other one - bool shouldTriggerAdhara = Bot.HasInMonstersZone(CardId.TenyiVishuda); - bool shouldTriggerVishuda = Bot.HasInMonstersZone(CardId.TenyiAdhara); - - if(shouldTriggerAdhara || shouldTriggerVishuda) + } + if (SwordsoulOfMoYeSummon() && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0) + { + summoned = true; + return true; + } + if (SwordsoulOfTaiaSummon() && CheckRemainInDeck(CardId.SwordsoulOfTaia) > 0) + { + summoned = true; return true; - - if(Bot.HasInMonstersZone(CardId.SwordsoulToken)) { - bool hasMaterial = Bot.HasInMonstersZone(CardId.SwordsoulTaia) || Bot.HasInMonstersZone(CardId.SwordsoulMoYe); - if(!hasMaterial) - return false; } - return true; + return false; } - private bool ActivateAshunaInGrave() { - if(!Card.Location.HasFlag(CardLocation.Grave)) + public bool IncredibleEcclesiaTheVirtuousSpSummon() + { + if (CheckWhetherNegated()) + { return false; - - if(!ShouldActivateGraveAshuna()) + } + if (CheckAtAdvantage() && enemyActivateMaxxC && Util.IsTurn1OrMain2()) + { return false; + } - AshunaActivated |= ActivatedEffect.Second; return true; } - private ClientCard AshunaSearchSelection(IList cards) { - if(Bot.HasInMonstersZone(CardId.TenyiAdhara) && cards.ContainsCardWithId(CardId.TenyiVishuda)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiVishuda)); - - if(Bot.HasInMonstersZone(CardId.TenyiVishuda) && cards.ContainsCardWithId(CardId.TenyiAdhara)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAdhara)); - - if(cards.Count > 0) - return cards[Rand.Next(cards.Count)]; + public bool AshBlossomActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (CheckAtAdvantage() && Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + return false; + } + if (DefaultAshBlossomAndJoyousSpring()) + { + CheckDeactiveFlag(); + return true; + } + return false; + } - return null; + public bool MaxxCActivate() + { + if (CheckWhetherNegated() || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); } - #endregion Ashuna Code + public bool EffectVeilerActivate() + { + if (CheckWhetherNegated()) return false; - #region Vishuda Code + List shouldNegateList = GetMonsterListForTargetNegate(true); + if (shouldNegateList.Count() > 0) + { + ClientCard target = shouldNegateList[0]; + currentNegateMonsterList.Add(target); + AI.SelectCard(target); + return true; + } - private bool SpecialSummonVishuda() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; + return false; + } - if(HasEffectMonster()) + public bool TunerForSynchroSummon() + { + if (!Card.IsCode(_CardId.AshBlossom) && !Card.IsCode(CardId.TenyiSpirit_Adhara) && !Card.IsCode(_CardId.EffectVeiler)) + { return false; - - if(VishudaActivated.HasFlag(ActivatedEffect.First)) + } + // taia check + if (Bot.HasInExtra(CardId.MonkOfTheTenyi) && Bot.HasInHand(CardId.SwordsoulOfTaia) + && !activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0) + { return false; + } - if(ActivateDescription == Util.GetStringId(CardId.TenyiVishuda, 1)) + // already has tuner, skip (maybe affected by Dimensional Barrier) + if (Bot.GetMonsters().Any(card => card.IsFaceup() && card.IsTuner())) + { return false; + } - bool ashunaReady = !AshunaActivated.HasFlag(ActivatedEffect.Second) && Bot.HasInGraveyard(CardId.TenyiAshuna); - bool vesselReady = !VesselActivated.HasFlag(ActivatedEffect.First) && Bot.HasInHand(CardId.VesselForDragonCycle); + // level10 check + List checkOnField = new List{CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Ashuna}; + bool hasLevel7NonTuner = Bot.GetMonsters().Any(card => card.IsFaceup() && checkOnField.Contains(card.Id) && !card.IsTuner()); + if (hasLevel7NonTuner) + { + int totalLevel = Card.Level + 7; + if (Bot.ExtraDeck.Any(card => card.HasType(CardType.Synchro) && card.Level == totalLevel && (!onlyWyrmSpSummon || card.HasRace(CardRace.Wyrm)))) + { + summoned = true; + return true; + } + } - // Always activate if able if there is a Ashuna in grave or Vessel in hand - if(ashunaReady || vesselReady) { - AI.SelectYesNo(true); - VishudaActivated |= ActivatedEffect.First; - return true; + List checkNonTuner = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.IsTuner()).ToList(); + checkNonTuner.Sort(CardContainer.CompareCardAttack); + // level7 check + if (Bot.HasInExtra(CardId.YaziEvilOfTheYangZing) && GetProblematicEnemyCardList(true, true).Count() > 0) + { + foreach (ClientCard checkCard in checkNonTuner) + { + if (Card.Level + checkCard.Level == 7) + { + summoned = true; + return true; + } + } + } + // level 11 check + if (Bot.HasInExtra(CardId.PsychicEndPunisher)) + { + foreach (ClientCard checkCard in checkNonTuner) + { + if ((checkCard.IsDisabled() || !checkCard.HasType(CardType.Synchro)) && (Card.Level + checkCard.Level == 11)) + { + summoned = true; + return true; + } + } } - if(!AshunaActivated.HasFlag(ActivatedEffect.First) && Bot.HasInHand(CardId.TenyiAshuna)) - return false; + return false; + } - // Moye combo is better - if(Bot.Hand.ContainsCardWithId(CardId.SwordsoulMoYe) && SoulswordMaterialCountInHand() <= 2) + public bool WyrmForBlackoutSummon() + { + if (Card.Level > 4 || !Card.HasRace(CardRace.Wyrm)) + { return false; + } + if (Bot.HasInHandOrInSpellZone(CardId.SwordsoulBlackout) && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasRace(CardRace.Wyrm))) + { + summoned = true; + return true; + } + return false; + } - // Longyuan combo is better - if(Bot.Hand.ContainsCardWithId(CardId.SwordsoulLongYuan) && SoulswordMaterialCountInHand() <= 2) - return false; + public bool TenyiSpirit_AdharaActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.TenyiSpirit_Adhara, 0)) + { + // special summon + if (TenyiSpSummonForTaiaCheck()) + { + return true; + } - // Ecclesia combo is better - if(Bot.Hand.ContainsCardWithId(CardId.IncredibleEcclesia)) - return false; + } + else if (ActivateDescription == Util.GetStringId(CardId.TenyiSpirit_Adhara, 1) && Card.Location == CardLocation.Grave && CheckCalledbytheGrave(Card.Id) == 0) + { + // recycle + if (!activatedCardIdList.Contains(CardId.SwordsoulStrategistLongyuan) && SwordsoulOfMoYeEffectCheck() + && Bot.HasInBanished(CardId.SwordsoulStrategistLongyuan)) + { + AI.SelectCard(CardId.SwordsoulStrategistLongyuan); + return true; + } + if (!summoned) + { + if (!activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) && SwordsoulOfMoYeEffectCheck() + && Bot.HasInBanished(CardId.SwordsoulOfMoYe)) + { + AI.SelectCard(CardId.SwordsoulOfMoYe); + return true; + } + if (!activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && SwordsoulOfTaiaEffectCheck() + && Bot.HasInBanished(CardId.SwordsoulOfTaia)) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + return true; + } + } + List recycleList = new List{CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Ashuna}; + foreach (int recycle in recycleList) + { + if (Bot.HasInBanished(recycle)) + { + AI.SelectCard(recycle); + return true; + } + } + } - AI.SelectPosition(CardPosition.Defence); - AI.SelectYesNo(true); - VishudaActivated |= ActivatedEffect.First; - return true; + return false; } - private bool ActivateVishudaInGrave() { - if(!Card.Location.HasFlag(CardLocation.Grave)) - return false; - - if(Card.IsDisabled()) - return false; + public bool TenyiSpSummonForTaiaCheck() + { + if (!activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0) + { + bool hasTaia = (!summoned && Bot.HasInHand(CardId.SwordsoulOfTaia)) || Bot.HasInMonstersZone(CardId.SwordsoulOfTaia); + bool noTargetInGrave = !Bot.Graveyard.Any(card => card.HasRace(CardRace.Wyrm) || card.HasSetcode(SetcodeSwordsoul)); + bool hasInExtra = Bot.HasInExtra(CardId.MonkOfTheTenyi); + bool notLongyuan = activatedCardIdList.Contains(CardId.SwordsoulStrategistLongyuan) || !Bot.HasInHand(CardId.SwordsoulStrategistLongyuan); - if(!HasNonEffectMonster()) - return false; - - if(ShouldSummonYazi()) - return false; - - if(ShouldSummonBaronne()) - return false; - - if(CanSummonChenying()) - return false; - - if(CanActivateYaziDestruction()) - return false; - - if(Enemy.GetMonsters().Count == 0) - return false; - - ClientCard target = SelectAnEnemyCardForRemoval(); - if(target == null) - return false; - - AI.SelectCard(target); - - VishudaActivated |= ActivatedEffect.Second; - return true; - } - - #endregion Vishuda Code - - #region Adhara Code - - private bool SummonAdhara() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(ShouldActivateGraveAshuna()) - return false; - - if(NormalSummonUsed) - return false; - - if(!Bot.HasInMonstersZone(CardId.TenyiVishuda)) - return false; - - NormalSummonUsed = true; - return true; - } - - private bool SpecialSummonAdhara() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(HasEffectMonster()) - return false; - - if(AdharaActivated.HasFlag(ActivatedEffect.First)) - return false; - - bool ashunaReady = !AshunaActivated.HasFlag(ActivatedEffect.Second) && Bot.HasInGraveyard(CardId.TenyiAshuna); - bool vesselReady = !VesselActivated.HasFlag(ActivatedEffect.First) && Bot.HasInHand(CardId.VesselForDragonCycle); - - // Always activate if able if there is a Ashuna in grave or Vessel in hand - if(ashunaReady || vesselReady) { - AdharaActivated |= ActivatedEffect.First; - return true; - } - - if(!AshunaActivated.HasFlag(ActivatedEffect.First) && Bot.HasInHand(CardId.TenyiAshuna)) - return false; - - // Moye combo is better - if(Bot.Hand.ContainsCardWithId(CardId.SwordsoulMoYe) && SoulswordMaterialCountInHand() == 2) - return false; - - // Longyuan combo is better - if(Bot.Hand.ContainsCardWithId(CardId.SwordsoulLongYuan) && SoulswordMaterialCountInHand() == 2) - return false; - - // Ecclesia combo is better - if(Bot.Hand.ContainsCardWithId(CardId.IncredibleEcclesia)) - return false; - - AdharaActivated |= ActivatedEffect.First; - return true; - } - - private bool ActivateGraveAdhara() { - if(!Card.Location.HasFlag(CardLocation.Grave)) - return false; - - if(!HasNonEffectMonster()) - return false; - - if(AdharaActivated.HasFlag(ActivatedEffect.Second)) - return false; - - ClientCard moye = Bot.Banished.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulMoYe)); - if(!NormalSummonUsed && moye != null && MoYeActivated.HasFlag(ActivatedEffect.First)) { - AdharaActivated |= ActivatedEffect.Second; - AI.SelectCard(moye); - return true; - } - - ClientCard taia = Bot.Banished.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulTaia)); - if(!NormalSummonUsed && taia != null && TaiaActivated.HasFlag(ActivatedEffect.First)) { - AdharaActivated |= ActivatedEffect.Second; - AI.SelectCard(taia); - return true; - } - - ClientCard longyuan = Bot.Banished.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulLongYuan)); - if(longyuan != null && LongYuanActivated.HasFlag(ActivatedEffect.First)) { - AdharaActivated |= ActivatedEffect.Second; - AI.SelectCard(longyuan); - return true; - } - - return false; - } - - #endregion Adhara Code - - #region Monk Code - - private bool SpecialSummonMonk() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; - - if(!ShouldSpecialSummonMonk()) - return false; - - return true; - } - - private bool ShouldSpecialSummonMonk() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.TenyiMonk)) - return false; - - int[] monkMaterial = new int[] { - CardId.TenyiAshuna, - CardId.TenyiVishuda, - CardId.TenyiAdhara - }; - - if(!Bot.MonsterZone.IsExistingMatchingCard(card => monkMaterial.Contains(card.Id))) - return false; - - if(Bot.HasInMonstersZone(CardId.TenyiMonk)) - return false; - - if(Bot.HasInMonstersZone(CardId.YangZingBaxia)) - return false; - - return true; - } - - #endregion Monk Code - - #region Draco Beserker Code - - private bool SummonDraco() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; - - if(!ShouldSpecialSummonDraco()) - return false; - - return true; - } - - private bool ShouldSpecialSummonDraco() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.TenyiDracoBeserker)) - return false; - - if(ShouldSpecialSummonMonk()) - return false; - - if(ShouldSummonDragnite()) - return false; - - if(ShouldSummonChixiao()) - return false; - - if(GetSynchroMaterials(TargetSynchroLevel.Level_8_Swordsoul) == null) - return false; - - return true; - } - - #endregion Monk Code - - #region Ecclesia Code - - private bool SpecialSummonEcclesia() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(!ShouldSpecialSummonEcclesia()) - return false; - - EcclesiaActivated |= ActivatedEffect.First; - return true; - } - - private bool SummonEcclesia() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(ShouldActivateGraveAshuna() && !HasEffectMonster()) - return false; - - if(ShouldSummonBaronne()) - return false; - - if(ShouldSummonRuddyRose()) - return false; - - if(NormalSummonUsed) - return false; - - if(CanActivateYaziDestruction()) - return false; - - if(ShouldSummonYazi()) - return false; - - if(ShouldSummonBlackRose()) - return false; - - if(Bot.HasInHand(CardId.SwordsoulMoYe) && CanActivateMoYeFromHand()) - return false; - - if(!EcclesiaActivated.HasFlag(ActivatedEffect.First) && Enemy.GetMonsterCount() > Bot.GetMonsterCount()) - return false; - - if(EcclesiaActivated.HasFlag(ActivatedEffect.Second)) - return false; - - NormalSummonUsed = true; - return true; - } - - private bool ShouldSpecialSummonEcclesia() { - if(Enemy.GetMonsterCount() <= Bot.GetMonsterCount()) - return false; - - if(ShouldActivateGraveAshuna()) - return false; - - if(ShouldSummonBaronne()) - return false; - - if(ShouldSummonRuddyRose()) - return false; - - if(CanActivateYaziDestruction()) - return false; - - if(ShouldSummonYazi()) - return false; - - if(ShouldSummonBlackRose()) - return false; - - if(EcclesiaActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(EcclesiaActivated.HasFlag(ActivatedEffect.Second)) - return false; - - if(!Bot.Hand.ContainsCardWithId(CardId.IncredibleEcclesia)) - return false; - - return true; - } - - private bool ActivateEcclesiaSearch() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(Bot.Deck.Count == 0) - return false; - - if(EcclesiaActivated.HasFlag(ActivatedEffect.Second)) - return false; - - EcclesiaActivated |= ActivatedEffect.Second; - return true; - } - - private ClientCard EcclesiaSearchSelection(IList cards) { - if(cards.ContainsCardWithId(CardId.SwordsoulTaia) && CanActivateTaiaFromHand()) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulTaia)); - - if(cards.ContainsCardWithId(CardId.SwordsoulMoYe) && HasSoulswordMaterialInHand()) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulMoYe)); - - if(cards.ContainsCardWithId(CardId.SwordsoulTaia)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulTaia)); - - if(cards.ContainsCardWithId(CardId.SwordsoulLongYuan)) { - AI.SelectPosition(CardPosition.Defence); - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulLongYuan)); - } - - if(cards.Count > 0) - return cards[Rand.Next(cards.Count)]; - - return null; - } - - #endregion - - #region Chengying Code - - private bool SummonChengying() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; - - if(!CanSummonChenying()) - return false; - - AI.SelectMaterials(GetSynchroMaterials(TargetSynchroLevel.Level_10)); - return true; - } - - private bool CanSummonChenying() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.SwordsoulChengying)) - return false; - - if(ShouldSummonBaronne()) - return false; - - if(ShouldSummonRuddyRose()) - return false; - - if(GetSynchroMaterials(TargetSynchroLevel.Level_10) == null) - return false; - - return true; - } - - private bool ActivateChengyingEffects() { - if(Card.IsDisabled()) - return false; - - // always activate either of Chegnying effects - return Card.Location.HasFlag(CardLocation.MonsterZone); - } - - private List SelectChengyingTargets(IList cards) { - if(cards.Count == 0) - return null; - - if(cards[0].Location.HasFlag(CardLocation.Grave) && cards[0].Owner == 0) - return SelectChengyingAvoid(cards); - - return SelectChengyingBanish(cards); - } - - private List SelectChengyingAvoid(IList cards) { - if(cards.Count == 0) - return null; - - // blackout is just good to remove - if(cards.ContainsCardWithId(CardId.SwordsoulBlackout)) - return new List() { cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulBlackout)) }; - - // these don't do much so we can remove - int[] preferRemoving = new int[] { - CardId.PotOfDesires, - CardId.ForbiddenChalice, - CardId.InfiniteImpermanence, - CardId.VesselForDragonCycle, - CardId.SwordsoulEmergence, - CardId.SwordsoulSummit - }; - - ClientCard preferredRemove = cards.GetFirstMatchingCard(card => preferRemoving.Contains(card.Id)); - if(preferredRemove != null) - return new List() { preferredRemove }; - - // we don't have great options, just avoid these cards - int[] avoidRemoving = new int[] { - CardId.TenyiAdhara, - CardId.TenyiAshuna, - CardId.TenyiVishuda, - CardId.SwordsoulMoYe, - CardId.SwordsoulTaia, - CardId.SwordsoulChengying, - CardId.SwordsoulChixiao - }; - - preferredRemove = cards.GetFirstMatchingCard(card => !avoidRemoving.Contains(card.Id)); - if(preferredRemove != null) - return new List() { preferredRemove }; - - ChengyingActivated |= ActivatedEffect.First; - - return null; - } - - private List SelectChengyingBanish(IList cards) { - if(cards.Count == 0) - return null; - - ClientCard card = SelectAnEnemyCardForRemoval(); - if(card == null) - return null; - - ChengyingActivated |= ActivatedEffect.Second; - return new List() { card }; - } - - #endregion Chengying - - #region Chixiao Code - - private bool SummonChixiao() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; - - if(!ShouldSummonChixiao()) - return false; - - List targets = GetSynchroMaterials(TargetSynchroLevel.Level_8_Swordsoul); - if(targets != null) { - AI.SelectMaterials(targets); - return true; - } - - targets = GetSynchroMaterials(TargetSynchroLevel.Level_8_Tenki); - if(targets != null) { - AI.SelectMaterials(targets); - return true; - } - - return false; - } - - private bool ShouldSummonChixiao() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.SwordsoulChixiao)) - return false; - - if(ChixiaoActivated.HasFlag(ActivatedEffect.First)) - return false; - - bool hasSwordsoulMats = GetSynchroMaterials(TargetSynchroLevel.Level_8_Swordsoul) != null; - bool hasTenkiMats = GetSynchroMaterials(TargetSynchroLevel.Level_8_Tenki) != null && !Bot.ExtraDeck.ContainsCardWithId(CardId.YangZingBaxia); - if(!hasSwordsoulMats && !hasTenkiMats) - return false; - - return true; - } - - private bool ActivateChixiaoSearch() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(!InPostSummonEffect(CardId.SwordsoulChixiao)) - return false; - - if(Bot.Deck.Count == 0) - return false; - - if(ChixiaoActivated.HasFlag(ActivatedEffect.First)) - return false; - - ChixiaoActivated |= ActivatedEffect.First; - return true; - } - - private bool ActivateChixiaoNegate() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(InPostSummonEffect(CardId.SwordsoulChixiao)) - return false; - - if(Enemy.GetMonsters().Count == 0) - return false; - - if(ChixiaoActivated.HasFlag(ActivatedEffect.Second)) - return false; - - ClientCard cost = null; - - if(Bot.HasInGraveyard(CardId.SwordsoulBlackout)) - cost = Bot.Graveyard.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulBlackout)); - - if(cost == null && Bot.HasInGraveyard(CardId.SwordsoulEmergence)) - cost = Bot.Graveyard.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulEmergence)); - - if(cost == null && Bot.HasInGraveyard(CardId.SwordsoulTaia)) - cost = Bot.Graveyard.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulTaia)); - - if(cost == null) { - IList possibleCost = Bot.Graveyard.GetMatchingCards(card => Wyrms.Contains(card.Id)); - if(possibleCost.Count > 0) - cost = possibleCost[Rand.Next(possibleCost.Count)]; - } - - if(cost == null) { - IList possibleCost = Bot.Hand.GetMatchingCards(card => Wyrms.Contains(card.Id)); - if(possibleCost.Count > 0) - cost = possibleCost[Rand.Next(possibleCost.Count)]; - } - - if(cost == null) - return false; - - AI.SelectCard(cost); - - if(!MonsterNegateNext()) - return false; - - ChixiaoActivated |= ActivatedEffect.Second; - return true; - } - - private ClientCard ChixiaoSearchSelection(IList cards) { - AI.SelectOption(0); - - if(NormalSummonUsed && Bot.HasInMonstersZone(CardId.TenyiMonk) && !Bot.HasInHand(CardId.SwordsoulBlackout)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulBlackout)); - - if(NormalSummonUsed && Bot.HasInMonstersZone(CardId.SwordsoulChengying) && !Bot.HasInHand(CardId.SwordsoulBlackout)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulBlackout)); - - // extenders - bool hasActivateableLongyuan = !LongYuanActivated.HasFlag(ActivatedEffect.First) && Bot.HasInHand(CardId.SwordsoulLongYuan) && HasSoulswordMaterialInHand(CardId.SwordsoulLongYuan); - bool isDeckLonyaunActivatable = !LongYuanActivated.HasFlag(ActivatedEffect.First) && cards.ContainsCardWithId(CardId.SwordsoulLongYuan) && HasSoulswordMaterialInHand(); - if(isDeckLonyaunActivatable && !hasActivateableLongyuan) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulLongYuan)); - - if(!Bot.HasInHand(CardId.SwordsoulSummit) && cards.ContainsCardWithId(CardId.SwordsoulSummit) && GetBestSummitTargetInGrave().ShouldSearch) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulSummit)); - - if(!Bot.HasInHand(CardId.SwordsoulMoYe) && cards.ContainsCardWithId(CardId.SwordsoulMoYe) && CanActivateMoYeFromDeck() && !NormalSummonUsed) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulMoYe)); - - if(!Bot.HasInHand(CardId.SwordsoulTaia) && cards.ContainsCardWithId(CardId.SwordsoulTaia) && CanActivateTaiaFromHand() && !NormalSummonUsed) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulTaia)); - - // any other target - if(cards.ContainsCardWithId(CardId.SwordsoulBlackout)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulBlackout)); - - if(cards.ContainsCardWithId(CardId.SwordsoulSummit)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulBlackout)); - - if(cards.ContainsCardWithId(CardId.SwordsoulLongYuan)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulLongYuan)); - - if(cards.ContainsCardWithId(CardId.SwordsoulMoYe)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulMoYe)); - - if(cards.ContainsCardWithId(CardId.SwordsoulTaia)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulTaia)); - - if(cards.ContainsCardWithId(CardId.SwordsoulSummit)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulSummit)); - - if(cards.Count > 0) - return cards[Rand.Next(cards.Count)]; - - return null; - } - - #endregion Chixiao - - #region Longyaun Code - - private bool ActivateLongYuanSummon() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(Card.IsDisabled()) - return false; - - if(LongYuanActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(Bot.HasInMonstersZone(CardId.SwordsoulLongYuan)) - return false; - - if(!HasSoulswordMaterialInHand(CardId.SwordsoulLongYuan)) - return false; - - if(EmptyMainMonsterZones() < 2) - return false; - - if(ShouldSpecialSummonEcclesia()) - return false; - - if(CanActivateTaiaFromField()) - return false; - - if(ShouldSummonYazi()) - return false; - - if(ShouldSummonChixiao()) - return false; - - if(CanActivateYaziDestruction()) - return false; - - if(ShouldSummonBlackRose()) - return false; - - if(ShouldSpecialSummonMonk()) - return false; - - if(CanSummonChaoFeng()) - return false; - - if(ShouldSummonBaxia()) - return false; - - List priorityOfDiscards = new List(); - priorityOfDiscards.Add(CardId.SwordsoulLongYuan); - - if(NormalSummonUsed || MoYeActivated.HasFlag(ActivatedEffect.First)) - priorityOfDiscards.Add(CardId.SwordsoulMoYe); - - if(NormalSummonUsed || TaiaActivated.HasFlag(ActivatedEffect.First)) - priorityOfDiscards.Add(CardId.SwordsoulTaia); - - priorityOfDiscards.AddRange(Tenyis); - priorityOfDiscards.AddRange(SwordSouls); - priorityOfDiscards.AddRange(Wyrms); - - AI.SelectPosition(CardPosition.Defence); - AI.SelectCard(priorityOfDiscards); - AI.SelectYesNo(true); - - LongYuanActivated |= ActivatedEffect.First; - return true; - } - - private bool ActivateLongyuanDamage() { - if(!Card.Location.HasFlag(CardLocation.Grave)) - return false; - - if(Card.IsDisabled()) - return false; - - if(LongYuanActivated.HasFlag(ActivatedEffect.Second)) - return false; - - LongYuanActivated |= ActivatedEffect.Second; - return true; - } - - #endregion - - #region Taia Code - - private bool ActivateTaiaSummon() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(!CanActivateTaiaFromField()) - return false; - - if(Bot.HasInGraveyard(CardId.SwordsoulEmergence)) { - AI.SelectCard(CardId.SwordsoulEmergence); - TaiaActivated |= ActivatedEffect.First; - return true; - } - - if(Bot.HasInGraveyard(CardId.SwordsoulSummit)) { - AI.SelectCard(CardId.SwordsoulEmergence); - TaiaActivated |= ActivatedEffect.First; - return true; - } - - if(Bot.HasInGraveyard(CardId.SwordsoulBlackout)) { - AI.SelectCard(CardId.SwordsoulBlackout); - TaiaActivated |= ActivatedEffect.First; - return true; - } - - if(Bot.HasInGraveyard(CardId.SwordsoulTaia)) { - AI.SelectCard(CardId.SwordsoulTaia); - TaiaActivated |= ActivatedEffect.First; - return true; - } - - if(!Bot.HasInGraveyard(Wyrms)) - return false; - - int[] goodTenyis = new int[] { - CardId.TenyiAdhara, - CardId.TenyiAshuna, - CardId.TenyiVishuda - }; - - IOrderedEnumerable possibilities = Bot.Graveyard.GetMatchingCards(card => card.HasRace(CardRace.Wyrm) && !goodTenyis.Contains(card.Id)).OrderBy(card => card.Attack); - if(possibilities.Count() > 0) - AI.SelectCard(possibilities.First()); - - TaiaActivated |= ActivatedEffect.First; - return true; - } - - private bool ActivateTaiaMill() { - if(!Card.Location.HasFlag(CardLocation.Grave)) - return false; - - if(Card.IsDisabled()) - return false; - - TaiaActivated |= ActivatedEffect.Second; - return true; - } - - private ClientCard TaiaMillSelection(IList cards) { - if(cards.Count == 0) - return null; - - if(cards.ContainsCardWithId(CardId.SwordsoulMoYe)) { - if(ShouldSummonDragnite() || Bot.HasInMonstersZone(CardId.AdamancipatorDragnite)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulMoYe)); - - if(!Bot.HasInGraveyard(CardId.SwordsoulMoYe)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulMoYe)); - } - - bool canSeedVishuda = !Bot.HasInGraveyard(CardId.TenyiVishuda) && cards.ContainsCardWithId(CardId.TenyiVishuda); - bool canActivateVishuda = VishudaActivated.HasFlag(ActivatedEffect.Second) && Enemy.GetMonsters().Count > 0; - if(canSeedVishuda && canActivateVishuda) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiVishuda)); - - bool canSeedAshuna = !Bot.HasInGraveyard(CardId.TenyiAshuna) && cards.ContainsCardWithId(CardId.TenyiAshuna); - bool canActivateAshuna = AshunaActivated.HasFlag(ActivatedEffect.Second); - if(canSeedAshuna && canActivateAshuna) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAshuna)); - - bool canSeedAdhara = !Bot.HasInGraveyard(CardId.TenyiAdhara) && cards.ContainsCardWithId(CardId.TenyiAdhara); - bool canActivateAdhara = AdharaActivated.HasFlag(ActivatedEffect.Second); - if(canSeedAdhara && canActivateAdhara) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAdhara)); - - if(cards.ContainsCardWithId(CardId.SwordsoulMoYe)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulMoYe)); - - return cards[Rand.Next(cards.Count)]; - } - - private bool CanActivateTaiaFromHand() { - if(TaiaActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(!Bot.HasInGraveyard(SwordSouls) && !Bot.HasInGraveyard(Wyrms)) - return false; - - if(EmptyMainMonsterZones() < 2) - return false; - - return true; - } - - private bool CanActivateTaiaFromGrave() { - if(TaiaActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(!Bot.Graveyard.IsExistingMatchingCard(card => SwordSouls.Contains(card.Id) || Wyrms.Contains(card.Id), 2)) - return false; - - if(EmptyMainMonsterZones() < 2) - return false; - - return true; - } - - private bool SummonTaia() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(ShouldSpecialSummonEcclesia()) - return false; - - if(ShouldActivateGraveAshuna()) - return false; - - if(ShouldSummonBaronne()) - return false; - - if(ShouldSummonRuddyRose()) - return false; - - if(NormalSummonUsed) - return false; - - if(!CanActivateTaiaFromHand()) - return false; - - if(CanActivateYaziDestruction()) - return false; - - if(ShouldSummonYazi()) - return false; - - if(ShouldSummonBlackRose()) - return false; - - NormalSummonUsed = true; - return true; - } - - private bool CanActivateTaiaFromField() { - if(!Bot.HasInMonstersZone(CardId.SwordsoulTaia)) - return false; - - if(TaiaActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(!Bot.HasInGraveyard(SwordSouls) && !Bot.HasInGraveyard(Wyrms)) - return false; - - if(EmptyMainMonsterZones() == 0) - return false; - - return true; - } - - #endregion Taia - - #region Moye Code - - private bool SummonMoYe() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(ShouldActivateGraveAshuna()) - return false; - - if(ShouldSummonBaronne()) - return false; - - if(ShouldSummonRuddyRose()) - return false; - - if(ShouldSpecialSummonEcclesia()) - return false; - - if(NormalSummonUsed) - return false; - - if(!CanActivateMoYeFromHand()) - return false; - - if(CanActivateYaziDestruction()) - return false; - - if(ShouldSummonBlackRose()) - return false; - - if(ShouldSummonYazi()) - return false; - - NormalSummonUsed = true; - return true; - } - - private bool ActivateMoYeSummon() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(!CanActivateMoYeFromHand()) - return false; - - AI.SelectCard(CardLocation.Hand); - - MoYeActivated |= ActivatedEffect.First; - return true; - } - - private bool ActivateMoYeDraw() { - if(!Card.Location.HasFlag(CardLocation.Grave)) - return false; - - if(Card.IsDisabled()) - return false; - - if(MoYeActivated.HasFlag(ActivatedEffect.Second)) - return false; - - if(Bot.Deck.Count == 0) - return false; - - MoYeActivated |= ActivatedEffect.Second; - return true; - } - - private bool CanActivateMoYeFromHand() { - if(MoYeActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(!HasSoulswordMaterialInHand(CardId.SwordsoulMoYe)) - return false; - - if(EmptyMainMonsterZones() < 2) - return false; - - return true; - } - - private bool CanActivateMoYeFromDeck() { - if(MoYeActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(!HasSoulswordMaterialInHand()) - return false; - - if(EmptyMainMonsterZones() < 2) - return false; - - return true; - } - - private bool CanActivateMoYeFromSummit() { - if(MoYeActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(!HasSoulswordMaterialInHand(CardId.SwordsoulSummit)) - return false; - - if(EmptyMainMonsterZones() < 2) - return false; - - return true; - } - - #endregion Moye - - #region Emergence Code - - private bool ActivateEmergenceSearch() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(Card.IsDisabled()) - return false; - - if(EmergenceActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(!Bot.HasInHand(CardId.SwordsoulTaia) && !TaiaActivated.HasFlag(ActivatedEffect.First) && !NormalSummonUsed) { - AI.SelectCard(CardId.SwordsoulTaia); - EmergenceActivated |= ActivatedEffect.First; - return true; - } - - if(!Bot.HasInHand(CardId.SwordsoulMoYe) && !MoYeActivated.HasFlag(ActivatedEffect.First) && !NormalSummonUsed) { - AI.SelectCard(CardId.SwordsoulMoYe); - EmergenceActivated |= ActivatedEffect.First; - return true; - } - - if(!Bot.HasInHand(CardId.SwordsoulLongYuan) && !LongYuanActivated.HasFlag(ActivatedEffect.First)) { - AI.SelectCard(CardId.SwordsoulLongYuan); - EmergenceActivated |= ActivatedEffect.First; - return true; + if (hasTaia && noTargetInGrave && hasInExtra && notLongyuan) + { + return true; + } } - return false; } - private bool ActivateEmergencLevelDown() { - if(!Card.Location.HasFlag(CardLocation.Removed)) - return false; - - if(Card.IsDisabled()) - return false; - - if(EmergenceActivated.HasFlag(ActivatedEffect.Second)) - return false; - - if(Enemy.GetFieldCount() == 0) - return false; - - if(!TriggeLevelDownForYazi()) - return false; - - EmergenceActivated |= ActivatedEffect.Second; - return true; - } - - #endregion - - #region Blackout Code - - private bool SetBlackout() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(Bot.GetMonsters().GetMatchingCardsCount(card => card.HasRace(CardRace.Wyrm)) == 0) - return false; - - return DefaultSpellSet(); - } - - private bool BlackoutActivateDestroy() { - if(!Card.Location.HasFlag(CardLocation.SpellZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(Enemy.GetFieldCount() < 2) - return false; - - if(CanSummonChenying()) - return false; - - if(ShouldSummonBaronne()) - return false; - - if(ShouldSummonChixiao()) - return false; - - int[] disallowedTargets = new int[] { - CardId.SwordsoulChixiao, - CardId.TenyiDracoBeserker, - CardId.YangZingChaofeng - }; - - List targets = new List(); - - IList myCard = Bot.MonsterZone.GetMatchingCards(card => Wyrms.Contains(card.Id) && !disallowedTargets.Contains(card.Id)); - if(myCard.Count == 0) - return false; - - if(Bot.HasInMonstersZone(CardId.TenyiAdhara)) - targets.Add(myCard.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAdhara))); - else if(Bot.HasInMonstersZone(CardId.SwordsoulChengying) && !ChengyingActivated.HasFlag(ActivatedEffect.First)) - targets.Add(myCard.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulChengying))); - else - targets.Add(myCard.OrderBy(card => card.Attack).First()); - - ClientCard enemy1 = SelectAnEnemyCardForRemoval(); - if(enemy1 == null) - return false; - - ClientCard enemy2 = SelectAnEnemyCardForRemoval(new List() { enemy1 }); - if(enemy2 == null) - return false; - - targets.Add(enemy1); - targets.Add(enemy2); - - if(targets.Count < 3) - return false; - - AI.SelectCard(targets); - return true; - } - - private bool BlackoutActivateSummon() { - if(Card.IsDisabled()) - return false; - - return Card.Location.HasFlag(CardLocation.Removed); - } - - #endregion - - #region Summit Code - - private bool ActivateSummit() { - if(!Card.Location.HasFlag(CardLocation.Hand)) - return false; - - if(Card.IsDisabled()) - return false; - - if(SummitActivated.HasFlag(ActivatedEffect.First)) - return false; - - SummitTargetResult result = GetBestSummitTargetInGrave(); - if(!result.HasTarget) - return false; - - if(!result.IsPowerful) - return false; - - SummitActivated |= ActivatedEffect.First; - AI.SelectCard(result.Card); - return true; - } - - private bool ActivateSummitLevelDown() { - if(!Card.Location.HasFlag(CardLocation.Removed)) - return false; - - if(Card.IsDisabled()) - return false; - - if(SummitActivated.HasFlag(ActivatedEffect.Second)) - return false; - - if(Enemy.GetFieldCount() == 0) - return false; - - if(!TriggeLevelDownForYazi()) - return false; - - SummitActivated |= ActivatedEffect.Second; - return true; - } - - private SummitTargetResult GetBestSummitTargetInGrave() { - //check powerful cards - if(Bot.HasInGraveyard(CardId.SwordsoulChengying)) - return SummitTargetResult.PowerfulTarget(CardId.SwordsoulChengying); - - if(Bot.HasInGraveyard(CardId.SwordsoulChixiao)) - return SummitTargetResult.PowerfulTarget(CardId.SwordsoulChixiao); - - if(Bot.HasInGraveyard(CardId.SwordsoulTaia) && CanActivateTaiaFromGrave()) - return SummitTargetResult.PowerfulTarget(CardId.SwordsoulTaia); - - if(Bot.HasInGraveyard(CardId.SwordsoulMoYe) && CanActivateMoYeFromSummit()) - return SummitTargetResult.PowerfulTarget(CardId.SwordsoulMoYe); - - bool hasSynchro = Bot.MonsterZone.IsExistingMatchingCard(card => card.HasType(CardType.Synchro) && card.IsFaceup()); - IList possibleCards = Bot.Graveyard.GetMatchingCards(card => { - if(hasSynchro && card.HasRace(CardRace.Wyrm)) - return true; - - if(SwordSouls.Contains(card.Id) && card.IsMonster()) - return true; - - return false; - }); - - if(possibleCards.Count == 0) - return new SummitTargetResult { HasTarget = false }; - - // Get highest attack card - int cardId = possibleCards.OrderByDescending(card => { return card.Attack; }).First().Id; - return new SummitTargetResult { - Card = cardId, - HasTarget = true, - IsPowerful = false + public bool TenyiForShamanSpSummon() + { + List checkEffectDesc = new List{ + Util.GetStringId(CardId.TenyiSpirit_Adhara, 0), Util.GetStringId(CardId.TenyiSpirit_Vishuda, 0), + Util.GetStringId(CardId.TenyiSpirit_Ashuna, 0) }; - } - - private class SummitTargetResult { - public bool HasTarget { get; set; } - public int Card { get; set; } - public bool IsPowerful { get; set; } - - public bool ShouldSearch => HasTarget && IsPowerful; - - public static SummitTargetResult PowerfulTarget(int cardId) { - return new SummitTargetResult { - Card = cardId, - HasTarget = true, - IsPowerful = true - }; - } - } - - #endregion Summit Code - - #region Baxia Code - - private bool SummonBaxia() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; - - if(!ShouldSummonBaxia()) - return false; - - List materials = GetSynchroMaterials(TargetSynchroLevel.Level_8_Tenki); - if(materials == null) - return false; - - AI.SelectMaterials(materials); - return true; - } - - private bool ShouldSummonBaxia() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.YangZingBaxia)) - return false; - - if(GetSynchroMaterials(TargetSynchroLevel.Level_8_Tenki) == null) - return false; - - return true; - } - - private bool BaxiaActivatedShuffle() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(BaxiaActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(!InPostSummonEffect(CardId.YangZingBaxia)) - return false; - - List targets = new List(); - ClientCard target1 = SelectAnEnemyCardForRemoval(); - if(target1 != null) - targets.Add(target1); - - ClientCard target2 = SelectAnEnemyCardForRemoval(new List() { target1 }); - if(target2 != null) - targets.Add(target2); - - BaxiaActivated |= ActivatedEffect.First; - - if(targets.Count == 0) { - AI.SelectCard(new int[0]); + if (!checkEffectDesc.Contains((int)ActivateDescription) || summoned || !Bot.HasInExtra(CardId.ShamanOfTheTenyi) + || (CheckAtAdvantage() && enemyActivateMaxxC)) + { return false; } - - AI.SelectCard(targets); - AI.SelectYesNo(true); - return true; - } - - private bool BaxiaActivatedRevive() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(!BaxiaActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(BaxiaActivated.HasFlag(ActivatedEffect.Second)) - return false; - - if(!Bot.HasInMonstersZone(CardId.TenyiMonk)) - return false; - - if(!Bot.HasInGraveyard(CardId.TenyiAdhara)) - return false; - - ClientCard monk = Bot.MonsterZone.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiMonk)); - ClientCard adhara = Bot.Graveyard.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAdhara)); - AI.SelectCard(monk); - AI.SelectNextCard(adhara); - - BaxiaActivated |= ActivatedEffect.Second; - return true; - } - - #endregion Baxia Code - - #region Ruddy Rose Code - - private bool SummonRuddyRose() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; - - if(!ShouldSummonRuddyRose()) - return false; - - AI.SelectMaterials(GetSynchroMaterials(TargetSynchroLevel.Level_10)); - return true; - } - - private bool ShouldSummonRuddyRose() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.RuddyRoseDragon)) - return false; - - if(Enemy.Graveyard.Count < 10) - return false; - - if(AshunaActivated.HasFlag(ActivatedEffect.First)) - return false; - - if(GetSynchroMaterials(TargetSynchroLevel.Level_10) == null) - return false; - - return true; - } - - private bool ActivateRuddyRoseBanish() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(!InPostSummonEffect(CardId.RuddyRoseDragon)) - return false; - - return true; - } - - private bool ActivateRuddyRoseNegate() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(Util.GetLastChainCard() == null) - return false; - - AI.SelectYesNo(true); - return DefaultTrap(); - } - - #endregion Ruddy Rose Code - - #region Baronne Code - - private bool SummonBaronne() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; - - if(ShouldSummonRuddyRose()) - return false; - - if(!ShouldSummonBaronne()) - return false; - - AI.SelectMaterials(GetSynchroMaterials(TargetSynchroLevel.Level_10)); - return true; - } - - private bool ShouldSummonBaronne() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.BaronneDeFluer)) - return false; - - if(Enemy.GetFieldCount() == 0 && !Bot.HasInMonstersZone(CardId.SwordsoulChengying)) - return false; - - if(AshunaActivated.HasFlag(ActivatedEffect.Second)) - return false; - - if(GetSynchroMaterials(TargetSynchroLevel.Level_10) == null) - return false; - - return true; - } - - private bool ActivateBaronneDestroy() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - bool inMain = Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2; - if(!inMain) - return false; - - if(Util.GetLastChainCard() != null) - return false; - - if(Enemy.GetFieldCount() == 0) + ClientCard toSummonMoye = Bot.Hand.FirstOrDefault(card => card.IsCode(CardId.SwordsoulOfMoYe)); + if (toSummonMoye == null) + { return false; - - if(BaronneActivated.HasFlag(ActivatedEffect.First)) + } + List notRevealCheckList = new List{Card, toSummonMoye}; + if (!SwordsoulOfMoYeEffectCheck(notRevealCheckList) || activatedCardIdList.Contains(CardId.SwordsoulOfMoYe)) + { return false; - - ClientCard target = SelectAnEnemyCardForRemoval(); - if(target == null) + } + if (activatedCardIdList.Contains(CardId.SwordsoulOfTaia) || !Bot.HasInHandOrInGraveyard(CardId.SwordsoulOfTaia)) + { return false; - - BaronneActivated |= ActivatedEffect.First; - AI.SelectCard(target); - return true; - } - - private ClientCard BaronneDestroyTarget(IList cards) { - List targetAttempts = new List(); - for(int i = 0; i < Enemy.GetFieldCount(); i++) { - ClientCard target = SelectAnEnemyCardForRemoval(targetAttempts); - if(target != null && cards.Contains(target)) - return target; - - targetAttempts.Add(target); } - return null; + return true; } - private bool ActivateBaronneNegate() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; - - if(Card.IsDisabled()) - return false; - - if(Util.GetLastChainCard() == null) + public bool TenyiForBlackoutSpSummon() + { + List checkEffectDesc = new List{ + Util.GetStringId(CardId.TenyiSpirit_Adhara, 0), Util.GetStringId(CardId.TenyiSpirit_Vishuda, 0), + Util.GetStringId(CardId.TenyiSpirit_Ashuna, 0) + }; + if (!checkEffectDesc.Contains((int)ActivateDescription)) + { return false; - - if(BaronneActivated.HasFlag(ActivatedEffect.Second)) + } + if (CheckAtAdvantage() && enemyActivateMaxxC) + { return false; - - if(DefaultTrap()) { - BaronneActivated |= ActivatedEffect.Second; + } + if (Bot.HasInHandOrInSpellZone(CardId.SwordsoulBlackout) && !Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasRace(CardRace.Wyrm))) + { return true; } - return false; } - private bool ActivateBaronneRevive() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) + public bool PotOfDesiresActivate() + { + if (CheckWhetherNegated()) + { return false; + } + if (CheckAtAdvantage()) + { + bool result = Bot.Deck.Count() >= 15; + if (result) + { + SelectSTPlace(null, true); + } + return result; + } + SelectSTPlace(null, true); + return true; + } - if(Card.IsDisabled()) - return false; + public bool SwordsoulEmergenceActivate() + { + if (Card.Location == CardLocation.Removed) + { + return SwordsoulSpellBanishedEffect(); + } - if(!BaronneActivated.HasFlag(ActivatedEffect.Second)) - return false; + // Mo Ye + if (!Bot.HasInHand(CardId.SwordsoulOfMoYe) && !activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) + && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0 && SwordsoulOfMoYeEffectCheck()) + { + AI.SelectCard(CardId.SwordsoulOfMoYe); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); + return true; + } - if(Duel.Phase != DuelPhase.Standby) - return false; + // Taia + if (!Bot.HasInHand(CardId.SwordsoulOfTaia) && !activatedCardIdList.Contains(CardId.SwordsoulOfTaia) + && CheckRemainInDeck(CardId.SwordsoulOfTaia) > 0 && SwordsoulOfTaiaEffectCheck()) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); + return true; + } - ClientCard target = Bot.Graveyard.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiDracoBeserker)); - if(target != null) { - AI.SelectCard(target); - BaronneActivated &= ~ActivatedEffect.Second; - BaronneActivated |= ActivatedEffect.Third; + // Longyuan + if (!Bot.HasInHand(CardId.SwordsoulStrategistLongyuan) && !activatedCardIdList.Contains(CardId.SwordsoulStrategistLongyuan) + && CheckRemainInDeck(CardId.SwordsoulStrategistLongyuan) > 0 && SwordsoulOfMoYeEffectCheck()) + { + AI.SelectCard(CardId.SwordsoulStrategistLongyuan); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); return true; } - target = Bot.Graveyard.GetFirstMatchingCard(card => card.IsCode(CardId.AdamancipatorDragnite)); - if(target != null) { - AI.SelectCard(target); - BaronneActivated &= ~ActivatedEffect.Second; - BaronneActivated |= ActivatedEffect.Third; + // dump check + if (!Bot.HasInHand(CardId.SwordsoulOfMoYe) && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0 && SwordsoulOfMoYeEffectCheck()) + { + AI.SelectCard(CardId.SwordsoulOfMoYe); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); return true; } + List checkIdList = new List{CardId.SwordsoulOfTaia, CardId.SwordsoulOfMoYe, CardId.SwordsoulStrategistLongyuan}; + foreach (int checkId in checkIdList) + { + if (CheckRemainInDeck(checkId) > 0) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); + return true; + } + } return false; } - #endregion Baronne Code - - #region Yazi Code + public bool SwordsoulSacredSummitActivate() + { + if (Card.Location == CardLocation.Removed) + { + return SwordsoulSpellBanishedEffect(); + } + if (CheckAtAdvantage()) + { + if (enemyActivateMaxxC && Util.IsTurn1OrMain2()) + { + return false; + } + if (!activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) && Bot.HasInGraveyard(CardId.SwordsoulOfMoYe) + && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0 && SwordsoulOfMoYeEffectCheck()) + { + AI.SelectCard(CardId.SwordsoulOfMoYe); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); + return true; + } + if (!activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0) + { + ClientCard taia = Bot.Graveyard.FirstOrDefault(card => card.IsCode(CardId.SwordsoulOfTaia)); + if (taia != null && SwordsoulOfTaiaEffectCheck(taia)) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); + return true; + } + } + } + bool controlSynchro = Bot.GetMonsters().Any(card => card.IsFaceup() && card.HasType(CardType.Synchro)); - private bool SummonYazi() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; + List rebornTargetList = Bot.Graveyard.Where(card => + card.IsMonster() && (card.HasSetcode(SetcodeSwordsoul) || (controlSynchro && card.HasRace(CardRace.Wyrm)))).ToList(); + rebornTargetList.Sort(CardContainer.CompareCardAttack); + rebornTargetList.Reverse(); - if(!ShouldSummonYazi()) - return false; + if (rebornTargetList.Count() > 0) + { + ClientCard rebornTarget = rebornTargetList[0]; + if (rebornTarget.IsCode(CardId.SwordsoulOfMoYe) && (activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) || !SwordsoulOfMoYeEffectCheck())) + { + return false; + } + if (rebornTarget.IsCode(CardId.SwordsoulOfTaia) && activatedCardIdList.Contains(CardId.SwordsoulOfTaia)) + { + return false; + } + AI.SelectCard(rebornTargetList); + activatedCardIdList.Add(Card.Id); + SelectSTPlace(null, true); + return true; + } - AI.SelectMaterials(GetSynchroMaterials(TargetSynchroLevel.Level_7)); - return true; + return false; } - private bool ShouldSummonYazi() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.YangZingYazi)) - return false; + public bool SwordsoulSpellBanishedEffect() + { + // TODO + return false; + } - if(ShouldSummonBlackRose()) + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (CheckAtAdvantage() && Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { return false; + } + if (Duel.LastChainPlayer == 1) + { + // negate + if (Util.GetLastChainCard().IsMonster()) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + if (CheckCalledbytheGrave(code) > 0) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC) && CheckAtAdvantage()) + { + return false; + } + ClientCard graveTarget = Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.GetOriginCode() == code); + if (graveTarget != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(graveTarget); + currentNegatingIdList.Add(code); + CheckDeactiveFlag(); + return true; + } + } + + // banish target + foreach (ClientCard cards in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(cards) && cards.IsMonster()) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + int code = cards.Id; + AI.SelectCard(cards); + currentNegatingIdList.Add(code); + return true; + } + } - if(GetSynchroMaterials(TargetSynchroLevel.Level_7) == null) - return false; + // become targets + if (Duel.ChainTargets.Contains(Card)) + { + List enemyMonsters = Enemy.Graveyard.GetMatchingCards(card => card.IsMonster()).ToList(); + if (enemyMonsters.Count() > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + int code = enemyMonsters[0].Id; + AI.SelectCard(code); + currentNegatingIdList.Add(code); + return true; + } + } + } - if(Enemy.GetFieldCount() == 0) - return false; + // avoid danger monster in grave + if (Duel.LastChainPlayer == 1) return false; + List targets = CheckDangerousCardinEnemyGrave(true); + if (targets.Count() > 0) { + int code = targets[0].Id; + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + currentNegatingIdList.Add(code); + return true; + } - return true; + return false; } - private bool ActivateYaziDestruction() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) - return false; + public bool CrossoutDesignatorActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + // negate + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard() != null) + { + int code = Util.GetLastChainCard().Id; + int alias = Util.GetLastChainCard().Alias; + if (alias != 0 && alias - code < 10) code = alias; + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (CheckRemainInDeck(code) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectAnnounceID(code); + currentNegatingIdList.Add(code); + CheckDeactiveFlag(); + return true; + } + } + return false; + } - if(Card.IsDisabled()) - return false; + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; - if(!CanActivateYaziDestruction()) - return false; + ClientCard LastChainCard = Util.GetLastChainCard(); - ClientCard target = SelectAnEnemyCardForRemoval(); - if(target == null) - return false; + // negate spells + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ( (this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || Util.IsChainTarget(Card) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + CheckDeactiveFlag(); + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + if (target != null) + { + AI.SelectCard(target); + } else { + AI.SelectCard(Enemy.GetMonsters()); + } + infiniteImpermanenceList.Add(this_seq); + return true; + } + } + + // negate monster + List shouldNegateList = GetMonsterListForTargetNegate(false, true); + if (shouldNegateList.Count() > 0) + { + ClientCard negateTarget = shouldNegateList[0]; + currentNegateMonsterList.Add(negateTarget); + + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++ i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(negateTarget); + return true; + } - AI.SelectCard(Card); - AI.SelectNextCard(target); - return true; + return false; } - private bool CanActivateYaziDestruction() { - if(!Bot.HasInMonstersZone(CardId.YangZingYazi)) - return false; - - if(Enemy.GetFieldCount() == 0) - return false; + public bool SwordsoulBlackoutActivate() + { + // sp summon token + if (Card.Location == CardLocation.Removed) + { + AI.SelectPosition(CardPosition.FaceUpDefence); + activatedCardIdList.Add(Card.Id); + return true; + } - return true; - } + // self destroy targer + List selfDestroyList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasRace(CardRace.Wyrm)).ToList(); + selfDestroyList.Sort(CardContainer.CompareCardAttack); + ClientCard selfDestroyTarget = selfDestroyList[0]; + bool selfTargetIsImportant = selfDestroyTarget.HasType(CardType.Synchro); - private bool ActivateYaziSearch() { - if(Card.IsDisabled()) - return false; + // choose Chengying + List chengyingList = Bot.GetMonsters().Where(card => + card.IsCode(CardId.SwordsoulSupremeSovereign_Chengying) && card.IsFaceup() && !card.IsDisabled()).ToList(); + if (chengyingList.Count() > 0 && Bot.Graveyard.Count() > 0) + { + selfDestroyTarget = chengyingList[0]; + selfTargetIsImportant = false; + } + foreach (ClientCard selfCard in selfDestroyList) + { + if (Duel.LastChainTargets.Contains(selfCard)) + { + selfDestroyTarget = selfCard; + selfTargetIsImportant = false; + } + } - return Card.Location.HasFlag(CardLocation.Grave); - } + // destroy problem card + List problemCardList = GetProblematicEnemyCardList(true); + if (problemCardList.Count() >= 2 && Duel.Player == 1) + { + AI.SelectCard(selfDestroyTarget); + AI.SelectNextCard(problemCardList); + return true; + } + + List faceUpEnemyMonsterList = Enemy.GetMonsters().Where(card => card.IsFaceup()).ToList(); + faceUpEnemyMonsterList.Sort(CardContainer.CompareCardAttack); + faceUpEnemyMonsterList.Reverse(); + if (!selfTargetIsImportant && Duel.Player == 1) + { + // destroy multi monster + if (faceUpEnemyMonsterList.Count() >= 2) + { + AI.SelectCard(selfDestroyTarget); + AI.SelectNextCard(GetNormalEnemyTargetList()); + return true; + } - private ClientCard YaziSearchSelection(IList cards) { - if(cards.Count == 0) - return null; + // destroy card in EP + if (Duel.Phase == DuelPhase.End) + { + AI.SelectCard(selfDestroyTarget); + AI.SelectNextCard(GetNormalEnemyTargetList()); + return true; + } + } - if(!cards[0].Location.HasFlag(CardLocation.Deck)) - return null; + // destroy attack monster + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2 && faceUpEnemyMonsterList.Count() > 0) + { + int botBestAttack = Util.GetBestAttack(Bot); + int enemyBestAttack = faceUpEnemyMonsterList[0].GetDefensePower(); + if (enemyBestAttack >= botBestAttack) + { + AI.SelectCard(selfDestroyTarget); + AI.SelectNextCard(GetNormalEnemyTargetList()); + return true; + } + } - if(CanActivateMoYeFromDeck() && cards.ContainsCardWithId(CardId.SwordsoulMoYe)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulMoYe)); + return false; + } - if(CanActivateTaiaFromHand() && cards.ContainsCardWithId(CardId.SwordsoulTaia)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulTaia)); - if(LongYuanActivated.HasFlag(ActivatedEffect.First) && cards.ContainsCardWithId(CardId.SwordsoulLongYuan)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulLongYuan)); + public bool GeomathmechFinalSigmaSpSummon() + { + if (Bot.GetMonstersExtraZoneCount() > 0) return false; + bool hasFloowandereeze = Enemy.GetMonsters().Any(card => card.HasSetcode(SetcodeFloowandereeze)); + hasFloowandereeze |= Enemy.GetSpells().Any(card => card.HasSetcode(SetcodeFloowandereeze)); + hasFloowandereeze |= Enemy.Graveyard.Any(card => card.HasSetcode(SetcodeFloowandereeze)); + hasFloowandereeze |= Enemy.Banished.Any(card => card.HasSetcode(SetcodeFloowandereeze)); + if (hasFloowandereeze) + { + AI.SelectMaterials(GetSynchroMaterial(12)); + AI.SelectPosition(CardPosition.FaceUpAttack); + return true; + } - return null; + return false; } - private bool TriggeLevelDownForYazi() { - ClientCard selected = Bot.GetMonsters().GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulTaia)); - if(selected == null) - selected = Bot.GetMonsters().GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulMoYe)); - - if(selected == null) - return false; - - // blackrose condition, so we don't care about activations - if(Enemy.GetFieldCount() - Bot.GetFieldCount() >= 2) { - AI.SelectCard(selected); - AI.SelectOption(1); + public bool PsychicEndPunisherSpSummon() + { + List materialList = GetSynchroMaterial(11); + if (materialList.Count() > 1) + { + AI.SelectMaterials(materialList); + AI.SelectPosition(CardPosition.FaceUpAttack); return true; } - bool hasSynchroMaterial = Bot.HasInMonstersZone(CardId.SwordsoulToken) || (Util.GetLastChainCard() != null && Util.GetLastChainCard().Id == CardId.SwordsoulTaia); - if(!hasSynchroMaterial) - return false; + return false; + } - if(selected.Id == CardId.SwordsoulTaia && !CanActivateMoYeFromDeck()) - return false; + /// + /// remove level10 summon filter + /// + public bool Level10SpSummonCheckInit() + { + canSpSummonLevel10IdList.Clear(); + return false; + } + + /// + /// check which level10 monster can be summoned + /// + public bool Level10SpSummonCheckCount() + { + List checkIdList = new List{ + CardId.SwordsoulSupremeSovereign_Chengying, CardId.SwordsoulSinisterSovereign_QixingLongyuan, CardId.BaronneDeFleur + }; + foreach (int checkId in checkIdList) + { + if (Card.IsCode(checkId)) + { + canSpSummonLevel10IdList.Add(checkId); + } + } + return false; + } - if(selected.Id == CardId.SwordsoulMoYe && !CanActivateTaiaFromHand()) + /// + /// decide which level10 monster to summon + /// + public bool Level10SpSummonCheckDecide() + { + if (canSpSummonLevel10IdList.Count <= 1) + { return false; + } + List decideIdList = new List(); - AI.SelectCard(selected); - AI.SelectOption(1); + // BaronneDeFleur + if (canSpSummonLevel10IdList.Contains(CardId.BaronneDeFleur)) + { + // protect maxxc + if (Bot.HasInHand(_CardId.MaxxC)) + { + canSpSummonLevel10IdList.Clear(); + canSpSummonLevel10IdList.Add(CardId.BaronneDeFleur); + return false; + } - return true; - } + // continue to use taia for synchro + ClientCard taia = Bot.Graveyard.FirstOrDefault(card => card.IsCode(CardId.SwordsoulOfTaia)); + if (taia != null && SwordsoulOfTaiaEffectCheck(taia) && Bot.HasInHand(CardId.SwordsoulSacredSummit)) + { + canSpSummonLevel10IdList.Clear(); + canSpSummonLevel10IdList.Add(CardId.BaronneDeFleur); + return false; + } - #endregion Yazi Code + decideIdList.Add(CardId.BaronneDeFleur); + } - #region Chaofeng Code + // QixingLongyuan + if (canSpSummonLevel10IdList.Contains(CardId.SwordsoulSinisterSovereign_QixingLongyuan)) + { + if (CheckAtAdvantage()) + { + decideIdList.Add(CardId.SwordsoulSinisterSovereign_QixingLongyuan); + } + } - private bool SummonChaofeng() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; + // Chengying + if (canSpSummonLevel10IdList.Contains(CardId.SwordsoulSupremeSovereign_Chengying)) + { + int banishCount = Bot.Banished.Count() + Enemy.Banished.Count(); + // use blackout or chixiao to trigger + bool decideFlag = Bot.HasInHandOrInSpellZone(CardId.SwordsoulBlackout) + || Bot.HasInMonstersZone(CardId.SwordsoulGrandmaster_Chixiao, true, false, true); + if (CheckAtAdvantage()) + { + // overkill + if (3000 + banishCount * 100 >= Enemy.LifePoints) + { + decideFlag = true; + } + } else { + ClientCard enemyMonster = GetBestEnemyMonster(true); + if (enemyMonster != null && decideIdList.Count() == 0) + { + // for high-power monster + if (3000 + banishCount * 200 >= enemyMonster.GetDefensePower()) + { + decideFlag = true; + } + } + } - if(!CanSummonChaoFeng()) - return false; + if (decideFlag) + { + decideIdList.Add(CardId.SwordsoulSupremeSovereign_Chengying); + } + } - AI.SelectMaterials(GetSynchroMaterials(TargetSynchroLevel.Level_9)); - return true; + if (decideIdList.Count() > 0) + { + // if multi selections, select randomly + canSpSummonLevel10IdList.Clear(); + int index = Program.Rand.Next(decideIdList.Count()); + int lastDecide = decideIdList[index]; + canSpSummonLevel10IdList.Add(lastDecide); + } + return false; } - private bool ActivateChaofengSearchEffects() { - if(Card.IsDisabled()) - return false; + /// + /// perform level10 monster's synchro summon + /// + public bool Level10SpSummonCheckFinal() + { + if (canSpSummonLevel10IdList.Count() == 1) + { + int finalDecideId = canSpSummonLevel10IdList[0]; + if (Card.IsCode(finalDecideId)) + { + List materialList = GetSynchroMaterial(10, Card.IsCode(CardId.SwordsoulSinisterSovereign_QixingLongyuan)); + if (materialList.Count() > 1) + { + AI.SelectMaterials(materialList); + return true; + } + return true; + } + } - AI.SelectYesNo(true); - return true; + return false; } - private bool CanSummonChaoFeng() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.YangZingChaofeng)) + public bool AdamancipatorRisen_DragiteSpSummon() + { + if (!Bot.HasInMonstersZone(CardId.SwordsoulGrandmaster_Chixiao, true)) + { return false; - - if(GetSynchroMaterials(TargetSynchroLevel.Level_9) == null) + } + bool containWaterMonsterInGY = Bot.Graveyard.Any(card => card.IsMonster() && card.HasAttribute(CardAttribute.Water)); + bool canContainWaterInGY = containWaterMonsterInGY; + canContainWaterInGY |= Bot.GetMonsters().Any(card => card.HasAttribute(CardAttribute.Water) && card.IsFaceup()); + if (!canContainWaterInGY) + { return false; + } + SelectLevel8SynchroMaterial(false, !containWaterMonsterInGY); return true; } - private ClientCard SelectChaofengTarget(IList cards) { - if(cards.Count == 0) - return null; - - if(Card.Location.HasFlag(CardLocation.Grave)) - return SelectChaofengEffectForHand(cards); + public bool DracoBerserkerOfTheTenyiSpSummon() + { + if (CheckAtAdvantage() && enemyActivateMaxxC && Util.IsTurn1OrMain2()) + { + return false; + } - return SelectChaofengEffectForField(cards); + SelectLevel8SynchroMaterial(true); + return true; } - private ClientCard SelectChaofengEffectForHand(IList cards) { - bool canSummonEcclesia = !NormalSummonUsed || !EcclesiaActivated.HasFlag(ActivatedEffect.First); - bool canActivateEcclesia = canSummonEcclesia && !EcclesiaActivated.HasFlag(ActivatedEffect.Second); - bool doesWantEcclesia = Duel.Player == 1 || canActivateEcclesia; - if(cards.ContainsCardWithId(CardId.IncredibleEcclesia) && doesWantEcclesia) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.IncredibleEcclesia)); + public bool SwordsoulGrandmaster_ChixiaoSpSummon() + { + if (CheckAtAdvantage() && enemyActivateLockBird) + { + return false; + } + if (!activatedCardIdList.Contains(CardId.SwordsoulGrandmaster_Chixiao)) + { + SelectLevel8SynchroMaterial(true); + return true; + } - if(cards.ContainsCardWithId(CardId.AshBlossomAndJoyousSpring)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.AshBlossomAndJoyousSpring)); + return false; + } - if(cards.ContainsCardWithId(CardId.TenyiAdhara)) - return cards.GetFirstMatchingCard(card => card.IsCode(CardId.TenyiAdhara)); + public bool BaxiaBrightnessOfTheYangZingSpSummon() + { + if (CheckAtAdvantage()) + { + return false; + } - return null; - } + List problemList = GetProblematicEnemyCardList(true); + if (problemList.Count() > 1 && !activatedCardIdList.Contains(CardId.BaxiaBrightnessOfTheYangZing + 1)) + { + SelectLevel8SynchroMaterial(true); + return true; + } + if (problemList.Count() == 1 && Bot.GetSpellCount() > 0 && !activatedCardIdList.Contains(CardId.BaxiaBrightnessOfTheYangZing + 2)) + { + bool checkFlag = false; + if (!activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) && SwordsoulOfMoYeEffectCheck() && Bot.HasInGraveyard(CardId.SwordsoulOfMoYe)) + { + checkFlag = true; + } + if (!activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && Bot.HasInGraveyard(CardId.SwordsoulOfTaia)) + { + checkFlag = true; + } + if (checkFlag) + { + SelectLevel8SynchroMaterial(true); + return true; + } + } - private ClientCard SelectChaofengEffectForField(IList cards) { - // there is no decision to be made here, we have a single valid card of each attribute - return cards[0]; + return false; } - #endregion Chaofeng Code - - #region Dragnite Code - - private bool SummonDragnite() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; + public void SelectLevel8SynchroMaterial(bool needWyrmNonTuner = false, bool needWaterNonTuner = false) + { + List tunerList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.IsTuner() && card.Level < 8).ToList(); + List nonTunerList = Bot.GetMonsters().Where(card => card.IsFaceup() && !card.IsTuner() && card.Level < 8 + && (!needWyrmNonTuner || card.HasRace(CardRace.Wyrm) && (!needWaterNonTuner || card.HasAttribute(CardAttribute.Water)))).ToList(); + tunerList.Sort(CardContainer.CompareCardAttack); + nonTunerList.Sort(CardContainer.CompareCardAttack); + + List materialList = new List(); + foreach (ClientCard tuner in tunerList) + { + materialList.Clear(); + materialList.Add(tuner); + if (tuner.Level == 4) + { + // use moye first + if (activatedCardIdList.Contains(CardId.SwordsoulOfMoYe)) + { + ClientCard moye = nonTunerList.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulOfMoYe)); + if (moye != null) + { + materialList.Add(moye); + AI.SelectMaterials(materialList); + return; + } + } - if(!ShouldSummonDragnite()) - return false; + // use taia + if (activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && !needWaterNonTuner) + { + ClientCard taia = nonTunerList.GetFirstMatchingCard(card => card.IsCode(CardId.SwordsoulOfTaia)); + if (taia != null) + { + materialList.Add(taia); + AI.SelectMaterials(materialList); + return; + } + } + } - AI.SelectMaterials(GetSynchroMaterials(TargetSynchroLevel.Level_8_Swordsoul)); - return true; + foreach (ClientCard nonTuner in nonTunerList) + { + if (tuner.Level + nonTuner.Level == 8) + { + materialList.Add(nonTuner); + AI.SelectMaterials(materialList); + return; + } + } + } } - private bool ShouldSummonDragnite() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.AdamancipatorDragnite)) - return false; - - if(!Bot.HasInMonstersZone(CardId.SwordsoulChixiao)) - return false; - - bool canActivateDragnite = Bot.HasInMonstersZone(CardId.SwordsoulMoYe) || Bot.Graveyard.IsExistingMatchingCard(card => card.HasAttribute(CardAttribute.Water)); - if(!canActivateDragnite) - return false; - - if(AshunaActivated.HasFlag(ActivatedEffect.Second)) + public bool YaziEvilOfTheYangZingSpSummon() + { + if (Enemy.GetMonsterCount() + Enemy.GetSpellCount() == 0) + { return false; + } + bool shouldSummon = GetProblematicEnemyCardList(true, true).Count() > 0; + shouldSummon |= !activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0 + && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0 && SwordsoulOfMoYeEffectCheck(); + shouldSummon |= !activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0 + && CheckRemainInDeck(CardId.SwordsoulOfTaia) > 0; + + if (shouldSummon) + { + List materialList = GetSynchroMaterial(7); + if (materialList.Count() > 1) + { + AI.SelectMaterials(materialList); + return true; + } + } - if(GetSynchroMaterials(TargetSynchroLevel.Level_8_Swordsoul) == null) - return false; + return false; + } - return true; + public List GetSynchroMaterial(int level, bool needWyrmNonTuner = false) + { + List tunerList = Bot.GetMonsters().Where(card => + card.IsFaceup() && card.IsTuner() && !card.HasType(CardType.Xyz | CardType.Link)).ToList(); + List nonTunerList = Bot.GetMonsters().Where(card => + card.IsFaceup() && !card.IsTuner() && !card.HasType(CardType.Xyz | CardType.Link) && (!needWyrmNonTuner || card.HasRace(CardRace.Wyrm))).ToList(); + tunerList.Sort(CardContainer.CompareCardAttack); + nonTunerList.Sort(CardContainer.CompareCardAttack); + List selectList = new List(); + foreach (ClientCard tuner in tunerList) + { + selectList.Clear(); + selectList.Add(tuner); + foreach (ClientCard nonTuner in nonTunerList) + { + if (tuner.Level + nonTuner.Level == level && (nonTuner.IsDisabled() || !nonTuner.HasType(CardType.Synchro))) + { + selectList.Add(nonTuner); + return selectList; + } + } + } + selectList.Clear(); + return selectList; } - private bool ActivateDragniteNegate() { - if(!Card.Location.HasFlag(CardLocation.MonsterZone)) + public bool ShamanOfTheTenyiSpSummon() + { + if (CheckAtAdvantage() && enemyActivateMaxxC && Util.IsTurn1OrMain2()) + { + Logger.DebugWriteLine("[Shaman] advantage & maxxc, skip"); return false; - - if(Card.IsDisabled()) + } + // check extra summon + List extraZoneMonsters = Bot.GetMonstersInExtraZone(); + if (extraZoneMonsters.Count() > 0 && extraZoneMonsters.Any(card => card.IsFacedown() || !card.HasType(CardType.Link) || !card.HasRace(CardRace.Wyrm))) + { + Logger.DebugWriteLine("[Shaman] extra zone occupied, skip"); return false; - - if(Util.GetLastChainCard() == null) + } + // check spsummon target + bool hasSpSummonTaret = !activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0 + && Bot.HasInHandOrInGraveyard(CardId.SwordsoulOfTaia); + hasSpSummonTaret |= !activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0 + && Bot.HasInGraveyard(CardId.SwordsoulOfMoYe) && SwordsoulOfMoYeEffectCheck(); + hasSpSummonTaret |= Bot.GetGraveyardMonsters().Any(card => card.HasType(CardType.Synchro) && card.IsCanRevive() && card.HasRace(CardRace.Wyrm)); + if (!hasSpSummonTaret) + { + Logger.DebugWriteLine("[Shaman] no target, skip"); return false; + } - return DefaultTrap(); + // select material + List materialList = new List(extraZoneMonsters); + List mainMonsterZoneMonsters = Bot.GetMonstersInMainZone().Where(card => + card.IsFaceup() && !card.HasType(CardType.Synchro) && card.HasRace(CardRace.Wyrm)).ToList(); + mainMonsterZoneMonsters.Sort(CardContainer.CompareCardAttack); + materialList.AddRange(mainMonsterZoneMonsters); + if (materialList.Count() >= 2) + { + AI.SelectMaterials(materialList.GetRange(0, 2)); + return true; + } + + return false; } - #endregion Dragnite Code - - #region Black Rose Code - - private bool SummonBlackRose() { - if(!Card.Location.HasFlag(CardLocation.Extra)) - return false; - - if(!ShouldSummonBlackRose()) - return false; - - AI.SelectMaterials(GetSynchroMaterials(TargetSynchroLevel.Level_7)); - return true; + public bool MonkOfTheTenyiSpSummon() + { + List materialList = Bot.GetMonsters().Where(card => + card.IsFaceup() && !card.HasType(CardType.Synchro | CardType.Link) && card.HasSetcode(SetcodeTenyi)).ToList(); + if (materialList.Count() > 0) + { + materialList.Sort(CardContainer.CompareCardAttack); + AI.SelectMaterials(materialList); + return true; + } + return false; } - private bool ShouldSummonBlackRose() { - if(!Bot.ExtraDeck.ContainsCardWithId(CardId.BlackRoseDragon)) - return false; - if(Enemy.GetFieldCount() - Bot.GetFieldCount() < 2) + public bool PsychicEndPunisherActivate() + { + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + return true; + } + if (Bot.LifePoints <= 1500 || CheckWhetherNegated()) + { return false; - - if(GetSynchroMaterials(TargetSynchroLevel.Level_7) == null) + } + List selfBanishTarget = Bot.GetMonsters().Where(card => card != Card && (card.IsFacedown() || card.GetDefensePower() <= 1000)).ToList(); + if (selfBanishTarget.Count() == 0) + { return false; - + } + selfBanishTarget.Sort(CardContainer.CompareCardAttack); + AI.SelectCard(selfBanishTarget); + AI.SelectNextCard(GetNormalEnemyTargetList(true)); return true; } - private bool ActivateBlackroseDestroy() { - if(!Bot.HasInMonstersZone(CardId.BlackRoseDragon)) - return false; - - if(Card.IsDisabled()) - return false; + public bool SwordsoulSupremeSovereign_ChengyingActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.SwordsoulSupremeSovereign_Chengying, 0) || ActivateDescription == -1) + { + activatedCardIdList.Add(Card.Id); + List banishTargetList = Duel.CurrentChain.Where(card => card.Controller == 1 && card.Location == CardLocation.Grave).ToList(); + banishTargetList.AddRange(CheckDangerousCardinEnemyGrave(false)); + if (banishTargetList.Count() > 0) + { + ClientCard graveTarget = banishTargetList[0]; + Logger.DebugWriteLine("Chengying banish grave: " + graveTarget?.Name); + } + List fieldTargetList = GetNormalEnemyTargetList(); + if (fieldTargetList.Count() > 0) + { + ClientCard fieldTarget = fieldTargetList[0]; + Logger.DebugWriteLine("Chengying banish field: " + fieldTarget?.Name); + } + banishTargetList.AddRange(fieldTargetList); + AI.SelectCard(banishTargetList); - if(Enemy.GetFieldCount() - Bot.GetFieldCount() < 3) - return false; + } else if (ActivateDescription == hintReplaceDestroy) + { + List removeCardIdList = new List{ + _CardId.CalledByTheGrave, CardId.CrossoutDesignator, _CardId.InfiniteImpermanence, _CardId.AshBlossom, + _CardId.MaxxC, _CardId.EffectVeiler, CardId.MonkOfTheTenyi, CardId.ShamanOfTheTenyi, CardId.SwordsoulGrandmaster_Chixiao, + CardId.SwordsoulOfTaia, CardId.SwordsoulStrategistLongyuan, CardId.SwordsoulOfMoYe + }; + AI.SelectCard(removeCardIdList); + } else + { + Logger.DebugWriteLine("Chengying desc: " + ActivateDescription.ToString()); + } return true; } - #endregion Black Rose Code - - #region Utils - - private bool InPostSummonEffect(int cardId) { - IList summons = Util.Duel.LastSummonedCards; - if(summons.Count == 0) - return false; + public bool BaronneDeFleurActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.BaronneDeFleur, 1)) + { + // negate + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1) + { + ClientCard lastChainCard = Util.GetLastChainCard(); + if (CheckAtAdvantage() && lastChainCard.IsCode(_CardId.MaxxC)) + { + return false; + } + if (Duel.LastChainTargets.Contains(Card) && lastChainCard.IsCode(_CardId.EffectVeiler, _CardId.InfiniteImpermanence, _CardId.BreakthroughSkill)) + { + return false; + } + } + CheckDeactiveFlag(); + effectUsedBaronneDeFleurList.Add(Card); + return true; + } else if (Duel.Phase == DuelPhase.Standby) + { + // special summon after effect used + if (effectUsedBaronneDeFleurList.Contains(Card) && !CheckWhetherNegated()) + { + if (Duel.Player == 1) + { + if (!Bot.HasInMonstersZone(CardId.SwordsoulGrandmaster_Chixiao) && Bot.HasInGraveyard(CardId.SwordsoulGrandmaster_Chixiao)) + { + AI.SelectCard(CardId.SwordsoulGrandmaster_Chixiao); + return true; + } + } else if (GetProblematicEnemyCardList(true, true).Count() > 0) + { + return false; + } else if (CheckAtAdvantage()) { + if (Bot.ExtraDeck.Any(card => card.IsFacedown() && card.HasType(CardType.Synchro) && card.Level == 8)) + { + if (Bot.HasInGraveyard(CardId.SwordsoulOfMoYe) && SwordsoulOfMoYeEffectCheck() && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0) + { + AI.SelectCard(CardId.SwordsoulOfMoYe); + return true; + } + if (CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0) + { + ClientCard taia = Bot.Graveyard.FirstOrDefault(card => card.IsCode(CardId.SwordsoulOfTaia)); + if (taia != null && SwordsoulOfTaiaEffectCheck(taia)) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + return true; + } + } + } + } + } + } else { + // destroy + List targetList = GetNormalEnemyTargetList(); + if (targetList.Count() > 0) + { + AI.SelectCard(targetList); + return true; + } + } - ClientCard card = summons.Last(); - return card.IsCode(cardId) && card.Owner == 0; + return false; } - private bool HasSoulswordMaterialInHand(int activedSoulsword) { - // If there is more than one then we can activate just fine - if(Bot.Hand.IsExistingMatchingCard(card => card.IsCode(activedSoulsword), 2)) + public bool SwordsoulSinisterSovereign_QixingLongyuanActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.SwordsoulSinisterSovereign_QixingLongyuan, 0)) + { + // draw return true; - - if(Bot.Hand.IsExistingMatchingCard(card => !card.IsCode(activedSoulsword) && card.IsCode(Wyrms))) + } else if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.SwordsoulSinisterSovereign_QixingLongyuan, 1)) + { + // remove monster return true; + } else if (ActivateDescription == Util.GetStringId(CardId.SwordsoulSinisterSovereign_QixingLongyuan, 2)) + { + // remove spell/trap + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && lastChainCard.Controller == 1) + { + bool activateFlag = DefaultOnBecomeTarget(); + activateFlag |= Enemy.LifePoints <= 1200; + activateFlag |= lastChainCard.HasType(CardType.Continuous | CardType.Equip | CardType.Field | CardType.Pendulum); + if (activateFlag) + { + return true; + } + } + } else + { + Logger.DebugWriteLine("qixinglongyuan desc: " + ActivateDescription); + } - return Bot.Hand.IsExistingMatchingCard(card => !card.IsCode(activedSoulsword) && card.IsCode(SwordSouls)); - } - - private bool HasSoulswordMaterialInHand() { - return SoulswordMaterialCountInHand() > 0; + return false; } - private int SoulswordMaterialCountInHand() { - return Bot.Hand.GetMatchingCardsCount(card => SwordSouls.Contains(card.Id) || card.HasRace(CardRace.Wyrm)); - } + public bool AdamancipatorRisen_DragiteActivate() + { + // bounce + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.AdamancipatorRisen_Dragite, 0)) + { + if (CheckWhetherNegated()) + { + return false; + } + if (CheckRemainInDeck(CardId.NibiruThePrimalBeing) > 0 && (Enemy.GetMonsterCount() + Enemy.GetSpellCount() > 0)) + { + AI.SelectCard(GetNormalEnemyTargetList(false)); + return true; + } + return false; + } + + // negate + if (CheckWhetherNegated()) + { + return false; + } - private int EmptyMainMonsterZones() { - return 5 - Bot.GetMonstersInMainZone().Count; + return true; } - private ClientCard SelectAnEnemyCardForRemoval() { - return SelectAnEnemyCardForRemoval(new List()); + public bool DracoBerserkerOfTheTenyiActivate() + { + // do not banish Nibiru + ClientCard lastChainCard = Util.GetLastChainCard(); + if (lastChainCard != null && lastChainCard.IsCode(CardId.NibiruThePrimalBeing) && lastChainCard.Controller == 1) + { + return false; + } + return true; } - private ClientCard SelectEnemyMonsterForRemoval(List exclude) { - ClientCard bestTarget = Util.GetProblematicEnemyCard(canBeTarget: true); - if(bestTarget != null && bestTarget.IsMonster() && !exclude.Contains(bestTarget)) - return bestTarget; + public bool SwordsoulGrandmaster_ChixiaoActivate() + { + if (ActivateDescription == Util.GetStringId(CardId.SwordsoulGrandmaster_Chixiao, 1)) + { + // negate + if (CheckWhetherNegated()) return false; - IList monsters = Enemy.GetMonsters().GetMatchingCards(card => !card.IsFacedown() && !exclude.Contains(card)); - if(monsters.Count > 0) - return monsters.OrderByDescending(card => card.Attack).First(); + List negateTargetList = new List(); - // facedowns can't be filtered normally, so try again for facedowns - monsters = Enemy.GetMonsters().Where(card => card.IsFacedown() && !exclude.Contains(card)).ToList(); - if(monsters.Count > 0) - return monsters[Rand.Next(monsters.Count)]; + List shouldNegateList = GetMonsterListForTargetNegate(true); + if (shouldNegateList.Count() > 0) + { + ClientCard target = shouldNegateList[0]; + currentNegateMonsterList.Add(target); + negateTargetList.AddRange(shouldNegateList); + } - return null; - } + // negate unbreakable monster + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + bool botCanAttack = Bot.GetMonsters().Any(card => card.IsAttack()); + if (Duel.Player == 0 && botCanAttack) + { + negateTargetList.AddRange(Enemy.GetMonsters().Where(card => card.IsFaceup() && card.IsMonsterDangerous()).ToList()); + } + if (Duel.Player == 1) + { + ClientCard enemyMonster = Enemy.BattlingMonster; + if (enemyMonster != null && enemyMonster.IsMonsterInvincible()) + { + negateTargetList.Add(enemyMonster); + } + } + } - private ClientCard SelectAnEnemyCardForRemoval(List exclude) { - ClientCard bestTarget = Util.GetProblematicEnemyCard(canBeTarget: true); - if(bestTarget != null && !exclude.Contains(bestTarget)) - return bestTarget; + // trigger Chengying + if (Bot.HasInMonstersZone(CardId.SwordsoulSupremeSovereign_Chengying, true, false, true) + && !activatedCardIdList.Contains(CardId.SwordsoulSupremeSovereign_Chengying) && Enemy.Graveyard.Count() > 0) + { + if (GetProblematicEnemyMonster() != null || (Duel.Phase == DuelPhase.End && Duel.Player == 1)) + { + bool triggerFlag = true; + List enemyTargetList = Enemy.GetMonsters().Where(card => + card.IsFaceup() && card.HasType(CardType.Effect) && !card.IsShouldNotBeMonsterTarget() && card.IsShouldNotBeTarget()).ToList(); + if (enemyTargetList.Count() == 0) + { + List botTargetList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasType(CardType.Effect) + && !card.IsDisabled() && card != Card && !card.IsCode(CardId.SwordsoulSupremeSovereign_Chengying)).ToList(); + if (botTargetList.Count() == 0) + { + triggerFlag = false; + } else { + botTargetList.Sort(CardContainer.CompareCardAttack); + enemyTargetList.AddRange(botTargetList); + } + } else { + enemyTargetList.Sort(CardContainer.CompareCardAttack); + enemyTargetList.Reverse(); + } + if (triggerFlag) + { + negateTargetList.AddRange(enemyTargetList); + } + } + } - ClientCard monster = SelectEnemyMonsterForRemoval(exclude); - if(monster != null) - return monster; + if (negateTargetList.Count() > 0) + { + // select banish card + List graveBanishList = Bot.Graveyard.Where(card => card.HasSetcode(SetcodeSwordsoul) || card.HasRace(CardRace.Wyrm)).ToList(); + + if (graveBanishList.Count() > 0) + { + bool selectFlag = false; + // trigger blackout + ClientCard blackOut = graveBanishList.FirstOrDefault(card => card.IsCode(CardId.SwordsoulBlackout)); + if (Duel.Player == 0 && !activatedCardIdList.Contains(CardId.SwordsoulBlackout) && blackOut != null) + { + AI.SelectCard(blackOut); + selectFlag = true; + } + if (!selectFlag) + { + // banish dump card + List checkIdList = new List{ + CardId.SwordsoulEmergence, CardId.SwordsoulOfTaia, CardId.SwordsoulOfMoYe, CardId.SwordsoulStrategistLongyuan, CardId.MonkOfTheTenyi, + CardId.TenyiSpirit_Adhara, CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Ashuna + }; + foreach (int checkId in checkIdList) + { + List checkCardList = graveBanishList.Where(card => card.IsCode(checkId)).ToList(); + if (checkCardList.Count() > 1) + { + AI.SelectCard(checkCardList); + selectFlag = true; + break; + } + } + } + if (!selectFlag) + { + // banish exists card + List checkIdList = new List{ + CardId.SwordsoulEmergence, CardId.MonkOfTheTenyi, CardId.ShamanOfTheTenyi, CardId.SwordsoulOfTaia, + CardId.SwordsoulStrategistLongyuan, CardId.SwordsoulOfMoYe, CardId.TenyiSpirit_Adhara, CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Ashuna + }; + foreach (int checkId in checkIdList) + { + List checkCardList = graveBanishList.Where(card => card.IsCode(checkId)).ToList(); + if (checkCardList.Count() > 0) + { + AI.SelectCard(checkCardList); + selectFlag = true; + break; + } + } + } + if (!selectFlag) + { + AI.SelectCard(ShuffleCardList(graveBanishList)); + } + } + AI.SelectNextCard(negateTargetList); + activatedCardIdList.Add(Card.Id); + return true; + } - IList spells = Enemy.GetSpells().GetMatchingCards(card => !card.IsFacedown() && !exclude.Contains(card) && !isNonPermamentSpell(card)); - if(spells.Count > 0) - return spells[Rand.Next(spells.Count)]; + } else { + // search + if (CheckAtAdvantage() && enemyActivateMaxxC && Util.IsTurn1OrMain2()) + { + if (CheckRemainInDeck(CardId.SwordsoulBlackout) > 0) + { + AI.SelectCard(CardId.SwordsoulBlackout); + activatedCardIdList.Add(Card.Id); + return true; + } - // check the spell facedowns - spells = Enemy.GetSpells().Where(card => !exclude.Contains(card) && card.IsFacedown()).ToList(); - if(spells.Count > 0) - return spells[Rand.Next(spells.Count)]; + List searchIdList = new List{ + CardId.SwordsoulBlackout, CardId.SwordsoulOfMoYe, CardId.SwordsoulOfTaia, CardId.SwordsoulEmergence, + CardId.SwordsoulStrategistLongyuan + }; + foreach (int checkId in searchIdList) + { + if (CheckRemainInDeck(checkId) > 0 && !Bot.HasInHand(checkId)) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } - return null; - } + if (CheckAtAdvantage()) + { + if (!activatedCardIdList.Contains(CardId.SwordsoulStrategistLongyuan) && !Bot.HasInHand(CardId.SwordsoulStrategistLongyuan) + && SwordsoulOfMoYeEffectCheck() && CheckRemainInDeck(CardId.SwordsoulStrategistLongyuan) > 0) + { + AI.SelectCard(CardId.SwordsoulStrategistLongyuan); + activatedCardIdList.Add(Card.Id); + return true; + } - private bool isNonPermamentSpell(ClientCard card) { - // normal spell - if(card.Type == (int)CardType.Spell) - return true; + if (!activatedCardIdList.Contains(CardId.SwordsoulStrategistLongyuan) && Bot.HasInHand(CardId.SwordsoulStrategistLongyuan) + && !activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && !activatedCardIdList.Contains(CardId.SwordsoulSacredSummit)) + { + // ready for another level 8 synchro + if (Bot.HasInHandOrInGraveyard(CardId.SwordsoulOfTaia) && !Bot.HasInHand(CardId.SwordsoulSacredSummit)) + { + if (CheckRemainInDeck(CardId.SwordsoulSacredSummit) > 0) + { + AI.SelectCard(CardId.SwordsoulSacredSummit); + activatedCardIdList.Add(Card.Id); + return true; + } + } + if (!Bot.HasInHandOrInGraveyard(CardId.SwordsoulOfTaia) && Bot.HasInHand(CardId.SwordsoulSacredSummit)) + { + if (CheckRemainInDeck(CardId.SwordsoulOfTaia) > 0) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + } + + if (!Bot.HasInMonstersZone(CardId.SwordsoulToken) && Bot.HasInMonstersZone(CardId.SwordsoulStrategistLongyuan) + && Bot.HasInMonstersZone(CardId.SwordsoulStrategistLongyuan) && CheckRemainInDeck(CardId.SwordsoulBlackout) > 0 + && !activatedCardIdList.Contains(CardId.SwordsoulBlackout)) + { + Logger.DebugWriteLine("Chixiao banish blackout"); + AI.SelectCard(CardId.SwordsoulBlackout); + AI.SelectOption(1); + activatedCardIdList.Add(Card.Id); + return true; + } - // quickplay spell - if(card.HasType(CardType.Spell | CardType.QuickPlay)) - return true; + if (CheckAtAdvantage()) + { + List searchIdList = new List{ + CardId.SwordsoulBlackout, CardId.SwordsoulOfMoYe, CardId.SwordsoulOfTaia, CardId.SwordsoulEmergence, + CardId.SwordsoulStrategistLongyuan + }; + foreach (int checkId in searchIdList) + { + if (CheckRemainInDeck(checkId) > 0 && !Bot.HasInHand(checkId)) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } + + List checkIdList = new List{ + CardId.SwordsoulBlackout, CardId.SwordsoulOfMoYe, CardId.SwordsoulOfTaia, CardId.SwordsoulEmergence, + CardId.SwordsoulStrategistLongyuan + }; + foreach (int checkId in checkIdList) + { + if (CheckRemainInDeck(checkId) > 0 && !Bot.HasInHand(checkId)) + { + AI.SelectCard(checkId); + activatedCardIdList.Add(Card.Id); + return true; + } + } + } return false; } - private bool MonsterNegateNext() { - if(Duel.Player == 1) { - ClientCard target = Enemy.MonsterZone.GetShouldBeDisabledBeforeItUseEffectMonster(); - if(target != null) { - AI.SelectNextCard(target); + public bool BaxiaBrightnessOfTheYangZingActivate() + { + Logger.DebugWriteLine("Baxia desc: " + ActivateDescription.ToString()); + + if (ActivateDescription == Util.GetStringId(CardId.BaxiaBrightnessOfTheYangZing, 0)) + { + List enemyTargetList = GetNormalEnemyTargetList(true); + if (enemyTargetList.Count() > 0) + { + AI.SelectCard(enemyTargetList); + activatedCardIdList.Add(Card.Id + 1); return true; } - } - - ClientCard LastChainCard = Util.GetLastChainCard(); + } else + { + List destroyTarget = Bot.GetSpells(); + destroyTarget.AddRange(Bot.GetMonsters().Where(card => card.IsFacedown() || card.Attack <= 1000).ToList()); + if (destroyTarget.Count() == 0) + { + return false; + } - if(LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.MonsterZone && - !LastChainCard.IsDisabled() && !LastChainCard.IsShouldNotBeTarget() && !LastChainCard.IsShouldNotBeSpellTrapTarget()) { - AI.SelectNextCard(LastChainCard); - return true; - } + bool canUseMoye = !activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) + && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0 && SwordsoulOfMoYeEffectCheck(); + bool canUseTaia = !activatedCardIdList.Contains(CardId.SwordsoulOfTaia) + && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0 && SwordsoulOfTaiaEffectCheck(); - if(Bot.BattlingMonster != null && Enemy.BattlingMonster != null) { - if(!Enemy.BattlingMonster.IsDisabled() && Enemy.BattlingMonster.IsCode(_CardId.EaterOfMillions)) { - AI.SelectNextCard(Enemy.BattlingMonster); + if (canUseMoye && Bot.HasInGraveyard(CardId.SwordsoulOfMoYe)) + { + AI.SelectCard(destroyTarget); + AI.SelectNextCard(CardId.SwordsoulOfMoYe); + activatedCardIdList.Add(Card.Id + 2); + return true; + } + if (canUseTaia && Bot.HasInGraveyard(CardId.SwordsoulOfTaia)) + { + AI.SelectCard(destroyTarget); + AI.SelectNextCard(CardId.SwordsoulOfTaia); + activatedCardIdList.Add(Card.Id + 2); return true; } + if (Bot.HasInGraveyard(CardId.IncredibleEcclesiaTheVirtuous)) + { + // sp summon ecclesia for moye/taia + if (!activatedCardIdList.Contains(CardId.IncredibleEcclesiaTheVirtuous)) + { + if ((canUseMoye && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0) + || (canUseTaia && CheckRemainInDeck(CardId.SwordsoulOfTaia) > 0)) + { + AI.SelectCard(destroyTarget); + AI.SelectNextCard(CardId.IncredibleEcclesiaTheVirtuous); + activatedCardIdList.Add(Card.Id + 2); + return true; + } + } + // sp summon ecclesia as tuner + if (Bot.GetMonsters().Any(card => card.IsFaceup() && !card.IsTuner() && card.Level == 4)) + { + AI.SelectCard(destroyTarget); + AI.SelectNextCard(CardId.IncredibleEcclesiaTheVirtuous); + activatedCardIdList.Add(Card.Id + 2); + return true; + } + } } - if(Duel.Phase == DuelPhase.BattleStart && Duel.Player == 1 && - Enemy.HasInMonstersZone(_CardId.NumberS39UtopiaTheLightning, true)) { - AI.SelectNextCard(_CardId.NumberS39UtopiaTheLightning); + return false; + } + + public bool YaziEvilOfTheYangZingActivate() + { + if (Card.Location == CardLocation.Grave) + { + // special summon + if (!activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0 + && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0 && SwordsoulOfMoYeEffectCheck()) + { + AI.SelectCard(CardId.SwordsoulOfMoYe); + return true; + } + if (!activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && CheckRemainInDeck(CardId.SwordsoulOfTaia) > 0 + && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + return true; + } + if (Bot.HasInMonstersZone(CardId.SwordsoulToken)) + { + List specialSummonIdListForSynchro = new List{CardId.SwordsoulStrategistLongyuan, CardId.SwordsoulOfMoYe, CardId.SwordsoulOfTaia}; + foreach (int checkId in specialSummonIdListForSynchro) + { + if (CheckRemainInDeck(checkId) > 0) + { + AI.SelectCard(checkId); + return true; + } + } + } + List specialSummonIdList = new List{ + CardId.TenyiSpirit_Ashuna, CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Adhara, + CardId.SwordsoulStrategistLongyuan, CardId.SwordsoulOfMoYe, CardId.SwordsoulOfTaia + }; + foreach (int checkId in specialSummonIdList) + { + if (CheckRemainInDeck(checkId) > 0) + { + AI.SelectCard(checkId); + return true; + } + } + } else + { + // destroy + if (CheckWhetherNegated()) + { + return false; + } + bool selfDestroy = false; + if (!activatedCardIdList.Contains(CardId.SwordsoulOfMoYe) && CheckRemainInDeck(CardId.SwordsoulOfMoYe) > 0 + && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0 && SwordsoulOfMoYeEffectCheck()) + { + selfDestroy = true; + } + if (!activatedCardIdList.Contains(CardId.SwordsoulOfTaia) && CheckRemainInDeck(CardId.SwordsoulOfTaia) > 0 + && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0) + { + selfDestroy = true; + } + if (selfDestroy) + { + AI.SelectCard(Card); + } else + { + List YangZingList = Bot.GetMonsters().Where(card => card.IsFaceup() && card.HasSetcode(SetcodeYangZing)).ToList(); + YangZingList.Sort(CardContainer.CompareCardAttack); + AI.SelectCard(YangZingList); + } + AI.SelectNextCard(GetNormalEnemyTargetList(true)); return true; } return false; } - private bool HasNonEffectMonster() { - return Bot.MonsterZone.IsExistingMatchingCard(card => !card.HasType(CardType.Effect) && card.IsFaceup()); - } - - private bool HasEffectMonster() { - return Bot.MonsterZone.IsExistingMatchingCard(card => card.HasType(CardType.Effect) && card.IsFaceup()); - } + public bool ShamanOfTheTenyiActivate() + { + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + // destroy + AI.SelectCard(GetNormalEnemyTargetList()); + return true; + } else + { + // special summon + if (CheckAtAdvantage() && enemyActivateMaxxC && Util.IsTurn1OrMain2()) + { + return false; + } - private enum TargetSynchroLevel : int { - Level_10 = 10, - Level_9 = 9, - Level_8_Swordsoul = 8, - Level_8_Tenki = -8, - Level_7 = 7 - } + bool canUseMoye = Bot.HasInGraveyard(CardId.SwordsoulOfMoYe) && CheckCalledbytheGrave(CardId.SwordsoulOfMoYe) == 0 + && !activatedCardIdList.Contains(CardId.SwordsoulOfMoYe); + bool canUseTaia = Bot.HasInHandOrInGraveyard(CardId.SwordsoulOfTaia) && CheckCalledbytheGrave(CardId.SwordsoulOfTaia) == 0 + && !activatedCardIdList.Contains(CardId.SwordsoulOfTaia); + bool shouldDiscardTaia = !Bot.HasInGraveyard(CardId.SwordsoulOfTaia) && Bot.HasInHand(CardId.SwordsoulOfTaia); + List sortedReviveTargetList = Bot.GetGraveyardMonsters().Where(card => + card.IsCanRevive() && card.HasRace(CardRace.Wyrm)).ToList(); + sortedReviveTargetList.Sort(CardContainer.CompareCardAttack); + sortedReviveTargetList.Reverse(); + + if (CheckAtAdvantage()) + { + // try to kill + if (Duel.Turn > 1 && Enemy.GetMonsterCount() == 0) + { + int currentAttack = Util.GetTotalAttackingMonsterAttack(0); + if (currentAttack < Enemy.LifePoints) + { + List overkillList = sortedReviveTargetList.Where(card => + card.Attack + currentAttack >= Enemy.LifePoints).ToList(); + if (overkillList.Count() > 0) + { + SelectDiscardForShamanOfTheTenyi(shouldDiscardTaia); + AI.SelectNextCard(overkillList); + return true; + } + } + } - private List GetSynchroMaterials(TargetSynchroLevel level) { - List materials = new List(); + // for next synchro + if (canUseMoye) + { + SelectDiscardForShamanOfTheTenyi(); + AI.SelectNextCard(CardId.SwordsoulOfMoYe); + return true; + } + if (canUseTaia) + { + SelectDiscardForShamanOfTheTenyi(shouldDiscardTaia); + AI.SelectNextCard(CardId.SwordsoulOfTaia); + return true; + } - switch(level) { - case TargetSynchroLevel.Level_10: - if(Bot.HasInMonstersZone(CardId.SwordsoulToken, false, false, true)) { - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulToken))); + // choose max attack + SelectDiscardForShamanOfTheTenyi(); + AI.SelectNextCard(sortedReviveTargetList); + return true; - if(Bot.HasInMonstersZone(CardId.SwordsoulLongYuan, false, false, true)) - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulLongYuan))); + } else { + // reborn synchro monster + List synchroMonsterList = sortedReviveTargetList.Where(card => card.HasType(CardType.Synchro)).ToList(); + if (synchroMonsterList.Count() > 0) + { + SelectDiscardForShamanOfTheTenyi(); + AI.SelectNextCard(synchroMonsterList); + return true; } - break; - case TargetSynchroLevel.Level_9: - if(Bot.HasInMonstersZone(CardId.YangZingBaxia, false, false, true)) { - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.YangZingBaxia))); - if(Bot.HasInMonstersZone(CardId.TenyiAdhara, false, false, true)) - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.TenyiAdhara))); + // for next synchro + if (canUseMoye) + { + SelectDiscardForShamanOfTheTenyi(); + AI.SelectNextCard(CardId.SwordsoulOfMoYe); + return true; } - break; - case TargetSynchroLevel.Level_8_Swordsoul: - if(Bot.HasInMonstersZone(CardId.SwordsoulToken, false, false, true)) { - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulToken))); - - if(Bot.HasInMonstersZone(CardId.SwordsoulMoYe, false, false, true)) - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulMoYe))); - else if(Bot.HasInMonstersZone(CardId.SwordsoulTaia, false, false, true)) - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulTaia))); + if (canUseTaia) + { + SelectDiscardForShamanOfTheTenyi(shouldDiscardTaia); + AI.SelectNextCard(CardId.SwordsoulOfTaia); + return true; } - break; - case TargetSynchroLevel.Level_8_Tenki: - if(Bot.HasInMonstersZone(CardId.TenyiAdhara, false, false, true)) { - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.TenyiAdhara))); + } + } - if(Bot.HasInMonstersZone(CardId.TenyiVishuda, false, false, true)) - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.TenyiVishuda))); - else if(Bot.HasInMonstersZone(CardId.TenyiAshuna, false, false, true)) - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.TenyiAshuna))); + return false; + } + + public void SelectDiscardForShamanOfTheTenyi(bool useTaia = false) + { + if (useTaia) + { + AI.SelectCard(CardId.SwordsoulOfTaia); + } else { + // drop tenyi + List tenyiList = new List{CardId.TenyiSpirit_Vishuda, CardId.TenyiSpirit_Ashuna, CardId.TenyiSpirit_Adhara}; + foreach (int tenyiId in tenyiList) + { + if (Bot.HasInHand(tenyiId)) + { + AI.SelectCard(tenyiId); + return; } - break; - case TargetSynchroLevel.Level_7: - if(Bot.HasInMonstersZone(CardId.SwordsoulToken, false, false, true)) { - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulToken))); + } - if(Bot.HasInMonstersZone(CardId.SwordsoulMoYe, false, false, true)) - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulMoYe))); - else if(Bot.HasInMonstersZone(CardId.SwordsoulTaia, false, false, true)) - materials.Add(Bot.MonsterZone.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.SwordsoulTaia))); + // drop dump card + foreach (ClientCard hand in Bot.Hand) + { + if (Bot.Hand.Where(card => card.IsCode(hand.Id)).Count() > 1) + { + AI.SelectCard(hand); + return; } - break; - } + } - if(!VerifySynchroMaterials(materials, level)) - return null; + // check discard list + List discardList = new List{ + CardId.CrossoutDesignator, _CardId.PotOfDesires, CardId.TenyiSpirit_Ashuna, CardId.TenyiSpirit_Vishuda, + CardId.TenyiSpirit_Adhara, CardId.NibiruThePrimalBeing, CardId.SwordsoulSacredSummit, CardId.IncredibleEcclesiaTheVirtuous, + _CardId.InfiniteImpermanence, _CardId.CalledByTheGrave, CardId.SwordsoulOfTaia, CardId.SwordsoulOfMoYe, + CardId.SwordsoulStrategistLongyuan, _CardId.AshBlossom, _CardId.MaxxC, _CardId.EffectVeiler, + CardId.SwordsoulEmergence, CardId.SwordsoulBlackout + }; + foreach (int discardCheck in discardList) + { + if (Bot.HasInHand(discardCheck)) + { + AI.SelectCard(discardCheck); + return; + } + } - return materials; + } } - private ClientCard GetCurrentSearchCardFromChain() { - int[] searchCards = new int[] { - CardId.SwordsoulChixiao, - CardId.SwordsoulTaia, - CardId.IncredibleEcclesia, - CardId.TenyiAshuna, - CardId.VesselForDragonCycle, - CardId.SwordsoulChengying, - CardId.YangZingYazi, - CardId.YangZingChaofeng + public bool SpellSetCheck() + { + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + List onlyOneSetList = new List{ + CardId.SwordsoulBlackout }; + if (onlyOneSetList.Contains(Card.Id) && Bot.HasInSpellZone(Card.Id)) + { + return false; + } - if(EffectChain.Count == 0) { - foreach(ClientCard card in Duel.CurrentChain) { - if(card.Owner == 0 && searchCards.Contains(card.Id)) - EffectChain.AddLast(card); + // select place + if ((Card.IsTrap() || Card.HasType(CardType.QuickPlay))) + { + List avoid_list = new List(); + int setFornfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setFornfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setFornfiniteImpermanence); + return true; + } else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } else + { + SelectSTPlace(); } + return true; } - if (EffectChain.Count > 0) + else if (Enemy.HasInSpellZone(_CardId.AntiSpellFragrance, true) || Bot.HasInSpellZone(_CardId.AntiSpellFragrance, true)) { - ClientCard card = EffectChain.Last.Value; - EffectChain.RemoveLast(); - return card; + if (Card.IsSpell() && !Bot.HasInSpellZone(Card.Id)) + { + SelectSTPlace(); + return true; + } } - return Util.GetLastChainCard(); - } - - public override void OnChainEnd() { - EffectChain.Clear(); - } - - private bool VerifySynchroMaterials(List materials, TargetSynchroLevel level) { - if(materials.Count != 2) - return false; - - int combinedLevel = materials[0].Level + materials[1].Level; - if(combinedLevel != Math.Abs((int)level)) - return false; - - return true; + return false; } - private void ResetActivatedCount() { - typeof(GameAI) - .GetField("_activatedCards", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) - .SetValue(AI, new Dictionary()); + protected override bool DefaultSetForDiabellze() + { + if (base.DefaultSetForDiabellze()) + { + SelectSTPlace(null, true); + return true; + } + return false; } - - #endregion Utils } } diff --git a/Game/AI/Decks/TearlamentsExecutor.cs b/Game/AI/Decks/TearlamentsExecutor.cs index 02114abc..37332adc 100644 --- a/Game/AI/Decks/TearlamentsExecutor.cs +++ b/Game/AI/Decks/TearlamentsExecutor.cs @@ -2,7 +2,10 @@ using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using System.Linq; - +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +using System; namespace WindBot.Game.AI.Decks { [Deck("Tearlaments", "AI_Tearlaments")] @@ -179,8 +182,8 @@ public TearlamentsExecutor(GameAI ai, Duel duel) AddExecutor(ExecutorType.Activate, CardId.BaronnedeFleur, BaronnedeFleurEffect); AddExecutor(ExecutorType.Activate, CardId.ElderEntityNtss, ElderEntityNtssEffect); AddExecutor(ExecutorType.Activate, CardId.PredaplantDragostapelia, PredaplantDragostapeliaEffect); - AddExecutor(ExecutorType.Activate, CardId.HeraldofOrangeLight); - AddExecutor(ExecutorType.Activate, CardId.HeraldofGreenLight); + AddExecutor(ExecutorType.Activate, CardId.HeraldofOrangeLight, DefaultTrap); + AddExecutor(ExecutorType.Activate, CardId.HeraldofGreenLight, DefaultTrap); AddExecutor(ExecutorType.Activate, CardId.TearlamentsRulkallos, TearlamentsRulkallosEffect); AddExecutor(ExecutorType.Activate, CardId.FADawnDragster); AddExecutor(ExecutorType.Activate, CardId.PrimevalPlanetPerlereino, PrimevalPlanetPerlereinoEffect); @@ -272,6 +275,7 @@ public override void OnNewTurn() spsummoned = false; summon_SprightElf = false; TearlamentsKitkallos_summoned = false; + base.OnNewTurn(); } private List GetZoneCards(CardLocation loc, ClientField player) { @@ -1699,7 +1703,7 @@ private bool UnderworldGoddessoftheClosedWorldLinkSummon(bool filter = true) if (Duel.Turn == 1 || Enemy.GetMonsterCount() <= 0) return false; List e_cards = Enemy.GetMonsters().Where(card => card != null && card.IsFaceup() && card.IsAttack()).ToList(); List b_cards = Bot.GetMonsters().Where(card => card != null && card.IsFaceup() && card.IsAttack()).ToList(); - if ((e_cards.Count <= 0 || b_cards.Count <= 0) && Enemy.MonsterZone.GetDangerousMonster() == null) return false; + if (e_cards.Count <= 0 || b_cards.Count <= 0 || Enemy.MonsterZone.GetDangerousMonster() == null) return false; e_cards.Sort(CardContainer.CompareCardAttack); e_cards.Reverse(); b_cards.Sort(CardContainer.CompareCardAttack); @@ -2167,6 +2171,7 @@ private bool DivineroftheHeraldSummon() } private bool EvaEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; List cards = Bot.GetGraveyardMonsters().Where(card => card != null && card.HasAttribute(CardAttribute.Light) && card.HasRace(CardRace.Fairy) && card != Card).ToList(); if (cards.Count <= 0) return false; activate_Eva = true; @@ -2202,6 +2207,7 @@ private bool TearlamentsScreamEffect_1() } private bool MudoratheSwordOracleEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Card.Location == CardLocation.Hand) { if ((Bot.Hand.Count(card => card != null && card.Id == CardId.AgidotheAncientSentinel) <= 0 || activate_AgidotheAncientSentinel_2) @@ -2647,4 +2653,4 @@ private bool TearlamentsSulliekEffect() } } -} +} \ No newline at end of file diff --git a/Game/AI/Decks/ThunderDragonExecutor.cs b/Game/AI/Decks/ThunderDragonExecutor.cs index 3811681e..2969c559 100644 --- a/Game/AI/Decks/ThunderDragonExecutor.cs +++ b/Game/AI/Decks/ThunderDragonExecutor.cs @@ -1,4 +1,4 @@ -using YGOSharp.OCGWrapper.Enums; +using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; using System.Linq; using WindBot; @@ -278,6 +278,8 @@ public override void OnNewTurn() for (int i = 0; i < selectAtt.Count; i++) selectAtt[i] = false; + + base.OnNewTurn(); } private bool IsAvailableZone(int seq) { @@ -656,7 +658,7 @@ public override IList OnSelectCard(IList cards, int min, if (res.Count > 0) return Util.CheckSelectCount(res, cards, min, max); return null; } - if (false /*hint == HintMsg.OperateCard*/) + if (hint == HintMsg.OperateCard) { if (cards.Any(card => card != null && card.Location == CardLocation.Removed)) { @@ -889,6 +891,7 @@ private int GetLinkMark(int cardId) } private bool AshBlossomEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Duel.CurrentChain.Count > 0 && Duel.LastChainPlayer != 0; } public int CompareCardLink(ClientCard cardA, ClientCard cardB) @@ -1053,6 +1056,7 @@ private bool MekkKnightCrusadiaAvramaxSummon() } private bool GEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; return Duel.Player != 0; } private bool ThunderDragonColossusSummon_2() @@ -1101,6 +1105,7 @@ private bool PredaplantVerteAnacondaEffect() { if (ActivateDescription == Util.GetStringId(CardId.PredaplantVerteAnaconda, 1)) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (CheckRemainInDeck(CardId.ThunderDragonFusion) <= 0) return false; if (Bot.GetMonstersInMainZone().Count > 4 && Bot.GetMonstersInMainZone().Count(card => card != null && !card.IsExtraCard() && card.HasSetcode(0x11c) && card.HasType(CardType.Monster) && card.IsFaceup()) <= 0) return false; List g_card = Bot.Graveyard.ToList(); @@ -1148,6 +1153,7 @@ private bool CrossSheepEffect() } private bool KnightmareUnicornEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; List cards = new List(); cards.AddRange(Enemy.SpellZone); cards.AddRange(Enemy.MonsterZone); @@ -1598,6 +1604,7 @@ private bool ThunderDragonhawkEffect() { if (Card.Location == CardLocation.Hand) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; List banish_cards = new List(); List grave_cards = new List(); foreach (var card in Bot.Banished) @@ -1792,6 +1799,7 @@ private bool ThunderDragonroarEffect() { if (Card.Location == CardLocation.Hand) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (handActivated) return false; handActivated = true; activate_ThunderDragonroar = true; @@ -2035,6 +2043,7 @@ private bool ThunderDragonmatrixEffect_2() { if (Card.Location == CardLocation.Hand) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Duel.Player == 0) { if (IsShouldChainTunder()) @@ -2243,6 +2252,7 @@ private bool TheBystialLubellionEffect() { if (Card.Location == CardLocation.Hand) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (HasInZoneNoActivate(CardId.BystialMagnamhut,CardLocation.Deck) && !Bot.HasInHand(CardId.BystialMagnamhut)) AI.SelectCard(CardId.BystialMagnamhut); else if(HasInZoneNoActivate(CardId.BystialDruiswurm, CardLocation.Deck) && !Bot.HasInHand(CardId.BystialDruiswurm)) @@ -2420,6 +2430,7 @@ private bool ThunderDragondarkEffect_2() { if (Card.Location == CardLocation.Hand) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Duel.Player == 0) { if (IsShouldChainTunder()) @@ -2498,6 +2509,7 @@ private bool ThunderDragondarkEffect() } private bool NormalThunderDragonEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; handActivated = true; ResetFlag(); selectFlag[(int)Select.NormalThunderDragon] = true; diff --git a/Game/AI/Decks/TimeThiefExecutor.cs b/Game/AI/Decks/TimeThiefExecutor.cs index bdba09f1..b0426ec6 100644 --- a/Game/AI/Decks/TimeThiefExecutor.cs +++ b/Game/AI/Decks/TimeThiefExecutor.cs @@ -1,12 +1,10 @@ -using System; +using YGOSharp.OCGWrapper; using YGOSharp.OCGWrapper.Enums; using System.Collections.Generic; -using System.Diagnostics; using WindBot; using WindBot.Game; using WindBot.Game.AI; using System.Linq; -using System.Reflection; namespace WindBot.Game.AI.Decks { @@ -194,6 +192,7 @@ private bool RegulatorEffect() { if (Card.Location == CardLocation.MonsterZone) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; AI.SelectCard(Monsters.TimeThiefCronocorder); AI.SelectCard(Monsters.TimeThiefWinder); return true; @@ -324,7 +323,7 @@ private bool FoolishBurialTarget() AI.SelectCard(Monsters.PerformTrickClown); return true; } - + private bool TrickClownEffect() { if (Bot.LifePoints <= 1000) @@ -372,6 +371,7 @@ private bool ThunderKingRaiOhsummon() } private bool ThunderKingRaiOheff() { + if (DefaultOnlyHorusSpSummoning()) return false; if (Duel.SummoningCards.Count > 0) { foreach (ClientCard m in Duel.SummoningCards) @@ -404,7 +404,6 @@ public bool PotofExtravaganceActivate() } - } } diff --git a/Game/AI/Decks/ToadallyAwesomeExecutor.cs b/Game/AI/Decks/ToadallyAwesomeExecutor.cs index 595b5275..a043e89c 100644 --- a/Game/AI/Decks/ToadallyAwesomeExecutor.cs +++ b/Game/AI/Decks/ToadallyAwesomeExecutor.cs @@ -231,6 +231,7 @@ private bool SwapFrogEffect() } else { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Bot.HasInHand(CardId.DupeFrog)) { AI.SelectCard( @@ -267,6 +268,7 @@ private bool GraydleSlimeJrEffect() private bool RonintoadinEffect() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; AI.SelectPosition(CardPosition.FaceUpDefence); return true; } @@ -297,6 +299,7 @@ private bool ToadallyAwesomeEffect() { if (Duel.CurrentChain.Count > 0) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; // negate effect, select a cost for it List monsters = Bot.GetMonsters(); IList suitableCost = new[] { @@ -381,6 +384,7 @@ private bool ToadallyAwesomeEffect() } else if (Duel.Phase == DuelPhase.Standby) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; SelectXYZDetach(Card.Overlays); if (Duel.Player == 0) { diff --git a/Game/AI/Decks/TrickstarExecutor.cs b/Game/AI/Decks/TrickstarExecutor.cs index 4788fd16..34370ba1 100644 --- a/Game/AI/Decks/TrickstarExecutor.cs +++ b/Game/AI/Decks/TrickstarExecutor.cs @@ -3,6 +3,7 @@ using WindBot; using WindBot.Game; using WindBot.Game.AI; +using System.Linq; namespace WindBot.Game.AI.Decks { @@ -67,6 +68,7 @@ public int getLinkMarker(int id) return 1; } + List Impermanence_list = new List(); bool NormalSummoned = false; ClientCard stage_locked = null; bool pink_ss = false; @@ -76,8 +78,7 @@ public int getLinkMarker(int id) bool white_eff_used = false; bool lockbird_useful = false; bool lockbird_used = false; - int GraveCall_id = 0; - int GraveCall_count = 0; + List currentNegatingIdList = new List(); List SkyStrike_list = new List { 26077387, 8491308, 63288573, 90673288, @@ -190,21 +191,39 @@ public bool Five_Rainbow() return false; } - public int SelectSTPlace() + public int SelectSTPlace(ClientCard card = null, bool avoid_Impermanence = false) { - List list = new List { 0, 1, 2, 3, 4 }; + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoid_Impermanence && Impermanence_list.Contains(seq)) continue; + list.Add(seq); + } + } int n = list.Count; while (n-- > 1) { - int index = Program.Rand.Next(n + 1); - int temp = list[index]; - list[index] = list[n]; - list[n] = temp; + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoid_Impermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + return (int)System.Math.Pow(2, seq); + } } - foreach(int seq in list) + foreach (int seq in list) { - int zone = (int)System.Math.Pow(2, seq); - if (Bot.SpellZone[seq] == null) return zone; + return (int)System.Math.Pow(2, seq); } return 0; } @@ -228,9 +247,9 @@ public bool SpellSet() return false; } - public bool IsTrickstar(int id) + public bool IsTrickstar(ClientCard card) { - return (id == CardId.Yellow || id == CardId.Red || id == CardId.Pink || id == CardId.White || id == CardId.Stage || id == CardId.Re || id == CardId.Crown); + return card.HasSetcode(0x8d); } public bool field_activate() @@ -246,18 +265,18 @@ public bool field_activate() public bool spell_trap_activate() { if (Card.Location != CardLocation.SpellZone && Card.Location != CardLocation.Hand) return true; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Enemy.HasInMonstersZone(CardId.Exterio,true) && !Bot.HasInHandOrHasInMonstersZone(CardId.Ghost)) return false; if (Card.IsSpell()) { - if (Enemy.HasInMonstersZone(33198837, true) && !Bot.HasInHandOrHasInMonstersZone(CardId.Ghost)) return false; - if (Enemy.HasInSpellZone(61740673, true) || Bot.HasInSpellZone(61740673,true)) return false; + if (Enemy.HasInMonstersZone(_CardId.NaturiaBeast, true) && !Bot.HasInHandOrHasInMonstersZone(CardId.Ghost)) return false; + if (Enemy.HasInSpellZone(_CardId.ImperialOrder, true) || Bot.HasInSpellZone(_CardId.ImperialOrder, true)) return false; if (Enemy.HasInMonstersZone(37267041, true) || Bot.HasInMonstersZone(37267041, true)) return false; return true; } if (Card.IsTrap()) { - if (Enemy.HasInSpellZone(51452091, true) || Bot.HasInSpellZone(51452091, true)) return false; - if (Enemy.HasInSpellZone(51452091, true) || Bot.HasInSpellZone(51452091, true)) return false; + if (Enemy.HasInSpellZone(_CardId.RoyalDecreel, true) || Bot.HasInSpellZone(_CardId.RoyalDecreel, true)) return false; return true; } // how to get here? @@ -505,7 +524,7 @@ public bool GalaxyCyclone() if (selected == null) return false; AI.SelectCard(selected); - AI.SelectPlace(SelectSTPlace()); + AI.SelectPlace(SelectSTPlace(Card, true)); return true; } @@ -526,13 +545,13 @@ public bool Feather_Act() if (self_card.IsCode(CardId.Galaxy)) return false; } - AI.SelectPlace(SelectSTPlace()); + AI.SelectPlace(SelectSTPlace(Card, true)); return true; } // activate when more than 2 cards if (Enemy.GetSpellCount() <= 1) return false; - AI.SelectPlace(SelectSTPlace()); + AI.SelectPlace(SelectSTPlace(Card, true)); return true; } @@ -602,7 +621,7 @@ public bool Stage_act() stage_locked = null; return true; } - if (Enemy.GetMonsterCount() > 0 && Util.GetBestEnemyMonster().Attack >= Util.GetBestAttack(Bot) && !Bot.HasInHand(CardId.White)) + if (Enemy.GetMonsterCount() > 0 && Util.GetBestEnemyMonster().Attack >= Util.GetBestAttack(Bot) && !Bot.HasInHand(CardId.White) && !DefaultCheckWhetherCardIdIsNegated(CardId.White)) { AI.SelectCard(CardId.White, CardId.Yellow, CardId.Pink, CardId.Red); stage_locked = null; @@ -627,7 +646,7 @@ public bool Pot_Act() if (!spell_trap_activate()) return false; if (Bot.Deck.Count > 15) { - AI.SelectPlace(SelectSTPlace()); + AI.SelectPlace(SelectSTPlace(Card, true)); return true; } return false; @@ -635,7 +654,7 @@ public bool Pot_Act() public bool Hand_act_eff() { - if (GraveCall_count > 0 && GraveCall_id == Card.Id) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Card.IsCode(CardId.Urara) && Util.GetLastChainCard().HasSetcode(0x11e) && Util.GetLastChainCard().Location == CardLocation.Hand) // Danger! archtype hand effect return false; if (Card.IsCode(CardId.Urara) && Bot.HasInHand(CardId.LockBird) && Bot.HasInSpellZone(CardId.Re)) return false; @@ -655,7 +674,7 @@ public bool Exterio_counter() public bool G_act() { - return (Duel.Player == 1 && !(GraveCall_count > 0 && GraveCall_id == Card.Id)); + return Duel.Player == 1 && !DefaultCheckWhetherCardIsNegated(Card); } public bool Pink_eff() @@ -742,7 +761,7 @@ public void Red_SelectPos(ClientCard return_card = null) monster_list.Reverse(); foreach(ClientCard card in monster_list) { - if (IsTrickstar(card.Id) && card != return_card && card.HasPosition(CardPosition.Attack)) + if (IsTrickstar(card) && card != return_card && card.HasPosition(CardPosition.Attack)) { int this_power = (Bot.HasInHand(CardId.White) && !white_eff_used) ? (card.RealPower + card.Attack) : card.RealPower; if (this_power >= self_power) self_power = this_power; @@ -757,12 +776,13 @@ public void Red_SelectPos(ClientCard return_card = null) public bool Red_ss() { - if ((Util.ChainContainsCard(CardId.DarkHole) || Util.ChainContainsCard(99330325) || Util.ChainContainsCard(53582587)) && Util.ChainContainsCard(CardId.Red)) return false; + if ((Util.ChainContainsCard(CardId.DarkHole) || Util.ChainContainsCard(_CardId.InterruptedKaijuSlumber) || Util.ChainContainsCard(_CardId.TorrentialTribute)) && Util.ChainContainsCard(CardId.Red)) return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Duel.LastChainPlayer == 0 && Util.GetLastChainCard().IsCode(CardId.Red)) { foreach (ClientCard m in Bot.GetMonsters()) { - if (Util.IsChainTarget(m) && IsTrickstar(m.Id)) + if (Util.IsChainTarget(m) && IsTrickstar(m)) { AI.SelectCard(m); Red_SelectPos(); @@ -780,7 +800,7 @@ public bool Red_ss() ClientCard tosolve_enemy = Util.GetOneEnemyBetterThanMyBest(); foreach (ClientCard c in self_m) { - if (IsTrickstar(c.Id) && !c.IsCode(CardId.Red)) + if (IsTrickstar(c) && !c.IsCode(CardId.Red)) { if (c.Attacked) { @@ -834,7 +854,7 @@ public bool Red_ss() self_monster.Sort(CardContainer.CompareDefensePower); foreach(ClientCard card in self_monster) { - if (IsTrickstar(card.Id) && !card.IsCode(CardId.Red)) + if (IsTrickstar(card) && !card.IsCode(CardId.Red)) { AI.SelectCard(card); Red_SelectPos(card); @@ -914,10 +934,11 @@ public bool Yellow_eff() public bool White_eff() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Duel.Phase >= DuelPhase.Main2) return false; if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) { - if (Bot.BattlingMonster == null || Enemy.BattlingMonster == null || !IsTrickstar(Bot.BattlingMonster.Id) || Bot.BattlingMonster.HasPosition(CardPosition.Defence)) return false; + if (Bot.BattlingMonster == null || Enemy.BattlingMonster == null || !IsTrickstar(Bot.BattlingMonster) || Bot.BattlingMonster.HasPosition(CardPosition.Defence)) return false; if (Bot.BattlingMonster.Attack <= Enemy.BattlingMonster.RealPower && Bot.BattlingMonster.Attack + Bot.BattlingMonster.BaseAttack >= Enemy.BattlingMonster.RealPower) { white_eff_used = true; @@ -934,7 +955,7 @@ public bool White_eff() { ClientCard tosolve = Util.GetBestEnemyMonster(true); ClientCard self_card = Bot.GetMonsters().GetHighestAttackMonster(); - if (tosolve == null || self_card == null || (tosolve != null && self_card != null && !IsTrickstar(self_card.Id))) + if (tosolve == null || self_card == null || (tosolve != null && self_card != null && !IsTrickstar(self_card))) { if (Enemy.GetMonsters().GetHighestAttackMonster()== null || Enemy.GetMonsters().GetHighestDefenseMonster() == null || @@ -946,7 +967,7 @@ public bool White_eff() } else return false; } - if (tosolve != null && self_card != null && IsTrickstar(self_card.Id) && !tosolve.IsMonsterHasPreventActivationEffectInBattle()) + if (tosolve != null && self_card != null && IsTrickstar(self_card) && !tosolve.IsMonsterHasPreventActivationEffectInBattle()) { int defender_power = tosolve.GetDefensePower(); Logger.DebugWriteLine("battle check 0:" + Duel.Phase.ToString()); @@ -970,6 +991,7 @@ public bool White_eff() public bool LockBird_act() { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (Duel.Player == 0 || lockbird_used) return false; lockbird_useful = true; if (Bot.HasInSpellZone(CardId.Re)) @@ -1003,12 +1025,12 @@ public bool Crown_eff() if (!spell_trap_activate()) return false; if (Duel.Phase <= DuelPhase.Main1 && Ts_reborn()) { - AI.SelectPlace(SelectSTPlace()); + AI.SelectPlace(SelectSTPlace(Card, true)); return true; } return false; } - if (Bot.HasInHand(CardId.Pink) && GraveCall_id != CardId.Pink) + if (Bot.HasInHand(CardId.Pink) && DefaultCheckWhetherCardIdIsNegated(CardId.Pink)) { AI.SelectCard(CardId.Pink); return true; @@ -1017,7 +1039,7 @@ public bool Crown_eff() { foreach(ClientCard hand in Bot.Hand) { - if (hand.IsMonster() && IsTrickstar(hand.Id)) + if (hand.IsMonster() && IsTrickstar(hand)) { if (hand.Attack >= Enemy.LifePoints) return true; if (!hand.IsCode(CardId.Yellow)) @@ -1607,12 +1629,17 @@ public bool GraveCall_eff() if (!spell_trap_activate()) return false; if (Duel.LastChainPlayer == 1) { - if (Util.GetLastChainCard().IsMonster() && Enemy.HasInGraveyard(Util.GetLastChainCard().Id)) + if (Util.GetLastChainCard().IsMonster()) { - GraveCall_id = Util.GetLastChainCard().Id; - GraveCall_count = 2; - AI.SelectCard(GraveCall_id); - return true; + int code = Util.GetLastChainCard().GetOriginCode(); + if (CheckWhetherNegated(code)) return false; + ClientCard target = Enemy.Graveyard.GetFirstMatchingCard(c => c.GetOriginCode() == code); + if (target != null) + { + currentNegatingIdList.Add(code); + AI.SelectCard(target); + return true; + } } } return false; @@ -1634,14 +1661,14 @@ public bool DarkHole_eff() { if (enemy.IsMonsterDangerous()) { - AI.SelectPlace(SelectSTPlace()); + AI.SelectPlace(SelectSTPlace(Card, true)); return true; } if (enemy.IsFaceup() && (enemy.GetDefensePower() > bestenemy)) bestenemy = enemy.GetDefensePower(); } if (bestPower <= bestenemy) { - AI.SelectPlace(SelectSTPlace()); + AI.SelectPlace(SelectSTPlace(Card, true)); return true; } } @@ -1656,7 +1683,7 @@ public bool IsAllEnemyBetter() ClientCard card = Bot.MonsterZone[i]; if (card == null || card.Data == null) continue; int newPower = card.Attack; - if (IsTrickstar(card.Id) && Bot.HasInHand(CardId.White) && !white_eff_used) newPower += card.RealPower; + if (IsTrickstar(card) && Bot.HasInHand(CardId.White) && !white_eff_used) newPower += card.RealPower; if (newPower > bestPower) bestPower = newPower; } @@ -1667,7 +1694,7 @@ public bool MonsterRepos() { if (Card.IsCode(CardId.Eater)) return (!Card.HasPosition(CardPosition.Attack)); - if (IsTrickstar(Card.Id) && !white_eff_used && Bot.HasInHand(CardId.White) && Card.IsAttack() && Duel.Phase == DuelPhase.Main1) return false; + if (IsTrickstar(Card) && !white_eff_used && Bot.HasInHand(CardId.White) && Card.IsAttack() && Duel.Phase == DuelPhase.Main1) return false; if (Card.IsFaceup() && Card.IsDefense() && Card.Attack == 0) return false; @@ -1705,13 +1732,41 @@ public override void OnNewTurn() white_eff_used = false; lockbird_useful = false; lockbird_used = false; - if (GraveCall_count > 0) + Impermanence_list.Clear(); + currentNegatingIdList.Clear(); + base.OnNewTurn(); + } + + public override void OnChaining(int player, ClientCard card) + { + if (card == null) return; + + if (player == 1) { - if (--GraveCall_count <= 0) + if (card.IsCode(_CardId.InfiniteImpermanence)) { - GraveCall_id = 0; - } + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == card) + { + Impermanence_list.Add(4-i); + break; + } + } + } } + base.OnChaining(player, card); + } + + public override void OnChainEnd() + { + currentNegatingIdList.Clear(); + base.OnChainEnd(); + } + + public bool CheckWhetherNegated(int cardId) + { + return !DefaultCheckWhetherCardIdIsNegated(cardId) && !currentNegatingIdList.Contains(cardId); } public override BattlePhaseAction OnSelectAttackTarget(ClientCard attacker, IList defenders) @@ -1763,10 +1818,20 @@ public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender { if (!defender.IsMonsterHasPreventActivationEffectInBattle()) { - if (IsTrickstar(attacker.Id) && Bot.HasInHand(CardId.White) && !white_eff_used) - attacker.RealPower = attacker.RealPower + attacker.Attack; + if (IsTrickstar(attacker) && Bot.HasInHand(CardId.White) && !white_eff_used && !CheckWhetherNegated(CardId.White)) + attacker.RealPower += attacker.Attack; } return base.OnPreBattleBetween(attacker, defender); } + + protected override bool DefaultSetForDiabellze() + { + if (base.DefaultSetForDiabellze()) + { + AI.SelectPlace(SelectSTPlace(Card, true)); + return true; + } + return false; + } } } diff --git a/Game/AI/Decks/WitchcraftExecutor.cs b/Game/AI/Decks/WitchcraftExecutor.cs index dfb68e34..72ec7a23 100644 --- a/Game/AI/Decks/WitchcraftExecutor.cs +++ b/Game/AI/Decks/WitchcraftExecutor.cs @@ -21,30 +21,24 @@ public class CardId public const int Haine = 84523092; public const int Schmietta = 21744288; public const int Pittore = 95245544; - public const int AshBlossom_JoyousSpring = 14558127; public const int PSYGamma = 38814750; - public const int MaxxC = 23434538; public const int Potterie = 59851535; public const int Genni = 64756282; public const int Collaboration = 10805153; public const int ThatGrassLooksGreener = 11110587; - public const int LightningStorm = 14532163; public const int PotofExtravagance = 49238328; public const int DarkRulerNoMore = 54693926; public const int Creation = 57916305; public const int Reasoning = 58577036; public const int MetalfoesFusion = 73594093; public const int Holiday = 83301414; - public const int CalledbytheGrave = 24224830; public const int Draping = 56894757; - public const int CrossoutDesignator = 65681983; public const int Unveiling = 70226289; public const int MagiciansLeftHand = 13758665; public const int Scroll = 19673561; public const int MagiciansRestage = 40252269; public const int WitchcrafterBystreet = 83289866; public const int MagicianRightHand = 87769556; - public const int InfiniteImpermanence = 10045474; public const int Masterpiece = 55072170; public const int Patronus = 94553671; public const int BorreloadSavageDragon = 27548199; @@ -60,10 +54,7 @@ public class CardId public const int RelinquishedAnima = 94259633; public const int NaturalExterio = 99916754; - public const int NaturalBeast = 33198837; - public const int ImperialOrder = 61740673; public const int SwordsmanLV7 = 37267041; - public const int RoyalDecreel = 51452091; public const int Anti_Spell = 58921041; public const int Numbe41BagooskatheTerriblyTiredTapir = 90590303; public const int PerformapalFive_RainbowMagician = 19619755; @@ -84,7 +75,7 @@ public WitchcraftExecutor(GameAI ai, Duel duel) // clear AddExecutor(ExecutorType.Activate, CardId.DarkRulerNoMore, DarkRulerNoMoreActivate); - AddExecutor(ExecutorType.Activate, CardId.LightningStorm, LightningStormActivate); + AddExecutor(ExecutorType.Activate, _CardId.LightningStorm, LightningStormActivate); AddExecutor(ExecutorType.Activate, CardId.RelinquishedAnima); // counter & quick effect @@ -93,13 +84,13 @@ public WitchcraftExecutor(GameAI ai, Duel duel) AddExecutor(ExecutorType.Activate, CardId.Potterie, DeckSSWitchcraft); AddExecutor(ExecutorType.Activate, CardId.Genni, DeckSSWitchcraft); AddExecutor(ExecutorType.Activate, CardId.PSYGamma, PSYGammaActivate); - AddExecutor(ExecutorType.Activate, CardId.MaxxC, MaxxCActivate); + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); AddExecutor(ExecutorType.Activate, CardId.GolemAruru, GolemAruruActivate); AddExecutor(ExecutorType.Activate, CardId.BorreloadSavageDragon, BorreloadSavageDragonActivate); - AddExecutor(ExecutorType.Activate, CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); - AddExecutor(ExecutorType.Activate, CardId.AshBlossom_JoyousSpring, AshBlossom_JoyousSpringActivate); - AddExecutor(ExecutorType.Activate, CardId.CalledbytheGrave, CalledbytheGraveActivate); - AddExecutor(ExecutorType.Activate, CardId.CrossoutDesignator, CrossoutDesignatorActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossom_JoyousSpringActivate); + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, _CardId.CrossoutDesignator, CrossoutDesignatorActivate); AddExecutor(ExecutorType.Activate, CardId.MagicianRightHand, SpellsActivate); AddExecutor(ExecutorType.Activate, CardId.MagiciansLeftHand, SpellsActivate); AddExecutor(ExecutorType.Activate, CardId.Unveiling, UnveilingActivate); @@ -187,16 +178,13 @@ public WitchcraftExecutor(GameAI ai, Duel duel) List FirstCheckSS = new List(); List UseSSEffect = new List(); List ActivatedCards = new List(); - Dictionary CalledbytheGraveCount = new Dictionary(); - int CrossoutDesignatorTarget = 0; + List currentNegatingIdList = new List(); bool MadameVerreGainedATK = false; bool summoned = false; bool enemy_activate_MaxxC = false; bool enemy_activate_DimensionShifter = false; bool MagiciansLeftHand_used = false; bool MagicianRightHand_used = false; - ClientCard MagiciansLeftHand_negate = null; - ClientCard MagicianRightHand_negate = null; // go first public override bool OnSelectHand() @@ -207,90 +195,73 @@ public override bool OnSelectHand() // reset the negated card in case of activated again public override void OnChainEnd() { - if (MagiciansLeftHand_negate != null) - { - MagiciansLeftHand_used = true; - MagiciansLeftHand_negate = null; - } - if (MagicianRightHand_negate != null) - { - MagicianRightHand_used = true; - MagicianRightHand_negate = null; - } + currentNegatingIdList.Clear(); base.OnChainEnd(); } - // check whether enemy activate important card - public override void OnChaining(int player, ClientCard card) + public override void OnChainSolved(int chainIndex) { - if (card == null) return; - // MagiciansLeftHand / MagicianRightHand - if (!MagicianRightHand_used && card.IsSpell() && card.Controller == 1) + ChainInfo currentCard = Duel.GetCurrentSolvingChainInfo(); + if (currentCard != null && currentCard.ActivatePlayer == 1) { - if (Bot.MonsterZone.GetFirstMatchingCard(c => c.HasRace(CardRace.SpellCaster)) != null - && Bot.HasInSpellZone(CardId.MagicianRightHand, true)) + if (Duel.IsCurrentSolvingChainNegated()) { - Logger.DebugWriteLine("MagicianRightHand negate: " + card.Name ?? "???"); - MagicianRightHand_negate = card; - } - } - if (!MagiciansLeftHand_used && card.IsTrap() && card.Controller == 1) - { - if (Bot.MonsterZone.GetFirstMatchingCard(c => c.HasRace(CardRace.SpellCaster)) != null - && Bot.HasInSpellZone(CardId.MagiciansLeftHand, true)) - { - Logger.DebugWriteLine("MagiciansLeftHand negate: " + card.Name ?? "???"); - MagiciansLeftHand_negate = card; + // MagiciansLeftHand / MagicianRightHand + if (!MagicianRightHand_used && currentCard.IsSpell()) + { + if (Bot.MonsterZone.GetFirstMatchingCard(c => c.HasRace(CardRace.SpellCaster) && c.IsFaceup()) != null + && Bot.HasInSpellZone(CardId.MagicianRightHand, true)) + { + Logger.DebugWriteLine("MagicianRightHand negate: " + currentCard.RelatedCard.Name ?? "???"); + MagicianRightHand_used = true; + } + } + if (!MagiciansLeftHand_used && currentCard.IsTrap() && currentCard.ActivatePlayer == 1) + { + if (Bot.MonsterZone.GetFirstMatchingCard(c => c.HasRace(CardRace.SpellCaster) && c.IsFaceup()) != null + && Bot.HasInSpellZone(CardId.MagiciansLeftHand, true)) + { + Logger.DebugWriteLine("MagiciansLeftHand negate: " + currentCard.RelatedCard.Name ?? "???"); + MagiciansLeftHand_used = true; + } + } } - } - - if (player == 1 && card.Id == CardId.MaxxC && CheckCalledbytheGrave(CardId.MaxxC) == 0) - { - enemy_activate_MaxxC = true; - } - if (player == 1 && card.Id == CardId.DimensionShifter && CheckCalledbytheGrave(CardId.DimensionShifter) == 0) - { - enemy_activate_DimensionShifter = true; - } - if (player == 1 && card.Id == CardId.InfiniteImpermanence && CrossoutDesignatorTarget != CardId.InfiniteImpermanence) - { - for (int i = 0; i < 5; ++i) + if (!Duel.IsCurrentSolvingChainNegated()) { - if (Enemy.SpellZone[i] == card) + if (currentCard.IsCode(_CardId.MaxxC)) + enemy_activate_MaxxC = true; + if (currentCard.IsCode(CardId.DimensionShifter)) + enemy_activate_DimensionShifter = true; + if (currentCard.IsCode(_CardId.InfiniteImpermanence)) { - Impermanence_list.Add(4-i); - break; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == currentCard.RelatedCard) + { + Impermanence_list.Add(4 - i); + break; + } + } } } } - base.OnChaining(player, card); } // new turn reset public override void OnNewTurn() { - CrossoutDesignatorTarget = 0; MadameVerreGainedATK = false; summoned = false; enemy_activate_MaxxC = false; enemy_activate_DimensionShifter = false; MagiciansLeftHand_used = false; MagicianRightHand_used = false; - MagiciansLeftHand_negate = null; - MagicianRightHand_negate = null; Impermanence_list.Clear(); FirstCheckSS.Clear(); UseSSEffect.Clear(); ActivatedCards.Clear(); - // CalledbytheGrave refresh - List key_list = CalledbytheGraveCount.Keys.ToList(); - foreach (int dic in key_list) - { - if (CalledbytheGraveCount[dic] > 1) - { - CalledbytheGraveCount[dic] -= 1; - } - } + currentNegatingIdList.Clear(); + base.OnNewTurn(); } // power fix @@ -298,7 +269,7 @@ public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender { if (!defender.IsMonsterHasPreventActivationEffectInBattle()) { - if (!MadameVerreGainedATK && Bot.HasInMonstersZone(CardId.MadameVerre, true, false, true) && attacker.HasSetcode(Witchcraft_setcode)) + if (!MadameVerreGainedATK && Bot.HasInMonstersZone(CardId.MadameVerre, true, false, true) && attacker.HasSetcode(Witchcraft_setcode)) { attacker.RealPower += CheckPlusAttackforMadameVerre(); } @@ -365,7 +336,7 @@ public override IList OnSelectCard(IList cards, int min, break; } } - + // only special summon advance monster if (flag && levels.Count > 1) { @@ -380,7 +351,7 @@ public override IList OnSelectCard(IList cards, int min, { result.Add(checked_card[1]); return result; - } + } } for (int i = 0; i < 3; ++i) { @@ -422,10 +393,10 @@ public override CardPosition OnSelectPosition(int cardId, IList po { return base.OnSelectPosition(cardId, positions); } - if (!Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon) + if (!Enemy.HasInMonstersZone(_CardId.BlueEyesChaosMAXDragon) && (Duel.Player == 1 && (cardId == CardId.MadameVerre || Util.GetOneEnemyBetterThanValue(Data.Attack + 1) != null)) - || cardId == CardId.MaxxC || cardId == CardId.AshBlossom_JoyousSpring) + || cardId == _CardId.MaxxC || cardId == _CardId.AshBlossom) { return CardPosition.FaceUpDefence; } @@ -454,34 +425,19 @@ public List CardListShuffle(List list) // check negated time count of id public int CheckCalledbytheGrave(int id) { - if (!CalledbytheGraveCount.ContainsKey(id)) - { - return 0; - } - return CalledbytheGraveCount[id]; + if (currentNegatingIdList.Contains(id)) return 1; + if (DefaultCheckWhetherCardIdIsNegated(id)) return 1; + return 0; } // check enemy's dangerous card in grave public List CheckDangerousCardinEnemyGrave(bool onlyMonster = false) { - List result = Enemy.Graveyard.GetMatchingCards(card => + List result = Enemy.Graveyard.GetMatchingCards(card => (!onlyMonster || card.IsMonster()) && card.HasSetcode(0x11b)).ToList(); return result; } - // check whether negate maxxc and InfiniteImpermanence - public void CheckDeactiveFlag() - { - if (Util.GetLastChainCard() != null && Util.GetLastChainCard().Id == CardId.MaxxC && Duel.LastChainPlayer == 1) - { - enemy_activate_MaxxC = false; - } - if (Util.GetLastChainCard() != null && Util.GetLastChainCard().Id == CardId.DimensionShifter && Duel.LastChainPlayer == 1) - { - enemy_activate_DimensionShifter = false; - } - } - /// /// Check count of discardable spells for witchcraft monsters. /// @@ -514,7 +470,26 @@ public bool CheckLastChainNegated() ClientCard lastcard = Util.GetLastChainCard(); if (lastcard == null || lastcard.Controller != 1) return false; if (lastcard.IsMonster() && lastcard.HasSetcode(TimeLord_setcode) && Duel.Phase == DuelPhase.Standby) return false; - return lastcard == MagiciansLeftHand_negate || lastcard == MagicianRightHand_negate; + if (DefaultCheckWhetherCardIdIsNegated(lastcard.GetOriginCode())) return false; + + // MagiciansLeftHand / MagicianRightHand + if (!MagicianRightHand_used && lastcard.IsSpell()) + { + if (Bot.MonsterZone.GetFirstMatchingCard(c => c.HasRace(CardRace.SpellCaster) && c.IsFaceup()) != null + && Bot.HasInSpellZone(CardId.MagicianRightHand, true)) + { + return true; + } + } + if (!MagiciansLeftHand_used && lastcard.IsTrap()) + { + if (Bot.MonsterZone.GetFirstMatchingCard(c => c.HasRace(CardRace.SpellCaster) && c.IsFaceup()) != null + && Bot.HasInSpellZone(CardId.MagiciansLeftHand, true)) + { + return true; + } + } + return false; } /// @@ -529,7 +504,7 @@ public bool CheckLinkMaterialsMatch(int LinkCount, int MaterialCount, List= 6 ? 6 : spells_id.Count(); return max_hand * 1000; - + } /// @@ -706,12 +681,12 @@ public int CheckRemainInDeck(int id) return Bot.GetRemainingCount(CardId.Schmietta, 3); case CardId.Pittore: return Bot.GetRemainingCount(CardId.Pittore, 3); - case CardId.AshBlossom_JoyousSpring: - return Bot.GetRemainingCount(CardId.AshBlossom_JoyousSpring, 1); + case _CardId.AshBlossom: + return Bot.GetRemainingCount(_CardId.AshBlossom, 1); case CardId.PSYGamma: return Bot.GetRemainingCount(CardId.PSYGamma, 3); - case CardId.MaxxC: - return Bot.GetRemainingCount(CardId.MaxxC, 1); + case _CardId.MaxxC: + return Bot.GetRemainingCount(_CardId.MaxxC, 1); case CardId.Potterie: return Bot.GetRemainingCount(CardId.Potterie, 1); case CardId.Genni: @@ -720,8 +695,8 @@ public int CheckRemainInDeck(int id) return Bot.GetRemainingCount(CardId.Collaboration, 1); case CardId.ThatGrassLooksGreener: return Bot.GetRemainingCount(CardId.ThatGrassLooksGreener, 2); - case CardId.LightningStorm: - return Bot.GetRemainingCount(CardId.LightningStorm, 2); + case _CardId.LightningStorm: + return Bot.GetRemainingCount(_CardId.LightningStorm, 2); case CardId.PotofExtravagance: return Bot.GetRemainingCount(CardId.PotofExtravagance, 3); case CardId.DarkRulerNoMore: @@ -734,12 +709,12 @@ public int CheckRemainInDeck(int id) return Bot.GetRemainingCount(CardId.MetalfoesFusion, 1); case CardId.Holiday: return Bot.GetRemainingCount(CardId.Holiday, 3); - case CardId.CalledbytheGrave: - return Bot.GetRemainingCount(CardId.CalledbytheGrave, 3); + case _CardId.CalledByTheGrave: + return Bot.GetRemainingCount(_CardId.CalledByTheGrave, 3); case CardId.Draping: return Bot.GetRemainingCount(CardId.Draping, 1); - case CardId.CrossoutDesignator: - return Bot.GetRemainingCount(CardId.CrossoutDesignator, 2); + case _CardId.CrossoutDesignator: + return Bot.GetRemainingCount(_CardId.CrossoutDesignator, 2); case CardId.Unveiling: return Bot.GetRemainingCount(CardId.Unveiling, 1); case CardId.MagiciansLeftHand: @@ -752,8 +727,8 @@ public int CheckRemainInDeck(int id) return Bot.GetRemainingCount(CardId.WitchcrafterBystreet, 3); case CardId.MagicianRightHand: return Bot.GetRemainingCount(CardId.MagicianRightHand, 1); - case CardId.InfiniteImpermanence: - return Bot.GetRemainingCount(CardId.InfiniteImpermanence, 3); + case _CardId.InfiniteImpermanence: + return Bot.GetRemainingCount(_CardId.InfiniteImpermanence, 3); case CardId.Masterpiece: return Bot.GetRemainingCount(CardId.Masterpiece, 1); case CardId.Patronus: @@ -808,7 +783,7 @@ public bool SpellNegatable(bool isCounter = false, ClientCard target = null) { // target default set if (target == null) target = Card; - if (target.Id == CrossoutDesignatorTarget) return true; + if (CheckCalledbytheGrave(target.GetOriginCode()) > 0) return true; // won't negate if not on field if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; @@ -816,13 +791,13 @@ public bool SpellNegatable(bool isCounter = false, ClientCard target = null) if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !isCounter) return true; if (target.IsSpell()) { - if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; - if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(_CardId.NaturiaBeast, true)) return true; + if (Enemy.HasInSpellZone(_CardId.ImperialOrder, true) || Bot.HasInSpellZone(_CardId.ImperialOrder, true)) return true; if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; } if (target.IsTrap()) { - if (Enemy.HasInSpellZone(CardId.RoyalDecreel, true) || Bot.HasInSpellZone(CardId.RoyalDecreel, true)) return true; + if (Enemy.HasInSpellZone(_CardId.RoyalDecreel, true) || Bot.HasInSpellZone(_CardId.RoyalDecreel, true)) return true; } // how to get here? return false; @@ -835,7 +810,7 @@ public bool NegatedCheck(bool disablecheck = true){ if (Card.IsSpell() || Card.IsTrap()){ if (SpellNegatable()) return true; } - if (CheckCalledbytheGrave(Card.Id) > 0 || Card.Id == CrossoutDesignatorTarget){ + if (CheckCalledbytheGrave(Card.GetOriginCode()) > 0){ return true; } if (Card.IsMonster() && Card.Location == CardLocation.MonsterZone && Card.IsDefense()) @@ -858,27 +833,44 @@ public bool NegatedCheck(bool disablecheck = true){ /// Card to set(default current card) /// Whether need to avoid InfiniteImpermanence /// Whether need to avoid set in this place - public void SelectSTPlace(ClientCard card = null, bool avoid_Impermanence = false, List avoid_list=null) + public void SelectSTPlace(ClientCard card = null, bool avoid_Impermanence = false, List avoid_list = null) { - List list = new List { 0, 1, 2, 3, 4 }; + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoid_Impermanence && Impermanence_list.Contains(seq)) continue; + if (avoid_list != null && avoid_list.Contains(seq)) continue; + list.Add(seq); + } + } int n = list.Count; while (n-- > 1) { - int index = Program.Rand.Next(n + 1); - int temp = list[index]; - list[index] = list[n]; - list[n] = temp; + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; } - foreach (int seq in list) + if (avoid_Impermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) { - int zone = (int)System.Math.Pow(2, seq); - if (Bot.SpellZone[seq] == null) + foreach (int seq in list) { - if (card != null && card.Location == CardLocation.Hand && avoid_Impermanence && Impermanence_list.Contains(seq)) continue; - if (avoid_list != null && avoid_list.Contains(seq)) continue; + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); AI.SelectPlace(zone); return; - }; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; } AI.SelectPlace(0); } @@ -886,7 +878,7 @@ public void SelectSTPlace(ClientCard card = null, bool avoid_Impermanence = fals // Spell&trap's set public bool SpellSet(){ if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; - if (Card.Id == CardId.CrossoutDesignator && Duel.Turn >= 5) return false; + if (Card.Id == _CardId.CrossoutDesignator && Duel.Turn >= 5) return false; // set condition int[] activate_with_condition = { CardId.Masterpiece, CardId.Draping }; @@ -939,9 +931,9 @@ public bool SpellSet(){ Impermanence_set += (int)System.Math.Pow(2, 4 - i); } } - if (Bot.HasInHand(CardId.InfiniteImpermanence)) + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) { - if (Card.IsCode(CardId.InfiniteImpermanence)) + if (Card.IsCode(_CardId.InfiniteImpermanence)) { AI.SelectPlace(Impermanence_set); return true; @@ -1101,7 +1093,7 @@ public void SelectDiscardSpell() } else { - AI.SelectCard(CardId.ThatGrassLooksGreener, CardId.LightningStorm, CardId.PotofExtravagance, CardId.MagiciansLeftHand, CardId.MagicianRightHand, CardId.CrossoutDesignator, CardId.CalledbytheGrave); + AI.SelectCard(CardId.ThatGrassLooksGreener, _CardId.LightningStorm, CardId.PotofExtravagance, CardId.MagiciansLeftHand, CardId.MagicianRightHand, _CardId.CrossoutDesignator, _CardId.CalledByTheGrave); } } @@ -1151,7 +1143,7 @@ public bool SpellsActivatewithCounter() { if (SpellNegatable()) return false; if ((Card.Id == CardId.ThatGrassLooksGreener || Card.Id == CardId.Reasoning) && CheckWhetherWillbeRemoved()) return false; - int[] counter_cards = { CardId.PSYGamma, CardId.CalledbytheGrave, CardId.CrossoutDesignator }; + int[] counter_cards = { CardId.PSYGamma, _CardId.CalledByTheGrave, _CardId.CrossoutDesignator }; int count = Bot.Hand.GetMatchingCardsCount(card => counter_cards.Contains(card.Id)); count += Bot.SpellZone.GetMatchingCardsCount(card => counter_cards.Contains(card.Id)); if (count > 0 || Bot.Hand.GetCardCount(Card.Id) >= 2) @@ -1272,13 +1264,13 @@ public bool DeckSSWitchcraft() int best_power = Util.GetBestAttack(Bot); if (CheckRemainInDeck(CardId.Haine) > 0 && best_power < 2400) best_power = 2400; Logger.DebugWriteLine("less summon check: " + (best_power + extra_attack - 1000).ToString() + " to " + (best_power + extra_attack).ToString()); - if (Util.GetOneEnemyBetterThanValue(best_power) != null + if (Util.GetOneEnemyBetterThanValue(best_power) != null && Util.GetOneEnemyBetterThanValue(best_power + extra_attack) == null && Util.GetOneEnemyBetterThanValue(best_power + extra_attack - 1000) != null) { lesssummon = true; } - + // SS lower 4 if (!enemy_activate_MaxxC && !lesssummon && discardable_hands >= 2 && Duel.Player == 0) { @@ -1374,7 +1366,7 @@ public bool MadameVerreActivate() // negate battle related effect if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) { - if (Enemy.MonsterZone.GetFirstMatchingCard(card => + if (Enemy.MonsterZone.GetFirstMatchingCard(card => card.IsMonsterDangerous() || (Duel.Player == 0) && card.IsMonsterInvincible()) != null) { SelectDiscardSpell(); @@ -1477,7 +1469,7 @@ public bool HaineActivate() return true; } } - + // end check if (Duel.Player == 0 && Duel.Phase == DuelPhase.End) @@ -1537,7 +1529,7 @@ public bool SchmiettaActivate() bool can_find_Holiday = Bot.HasInHandOrInSpellZone(CardId.Holiday) || (can_recycle && Bot.HasInGraveyard(CardId.Holiday) && !(ActivatedCards.Contains(CardId.Holiday))); // monster check - if (Bot.HasInHand(important_witchcraft) && !Bot.HasInGraveyard(CardId.Pittore) + if (Bot.HasInHand(important_witchcraft) && !Bot.HasInGraveyard(CardId.Pittore) && !ActivatedCards.Contains(CardId.Pittore) && CheckRemainInDeck(CardId.Pittore) > 0 && can_find_Holiday){ AI.SelectCard(CardId.Pittore); ActivatedCards.Add(CardId.Schmietta); @@ -1650,7 +1642,6 @@ public bool PittoreActivate() public bool AshBlossom_JoyousSpringActivate() { if (NegatedCheck(true) || CheckLastChainNegated()) return false; - CheckDeactiveFlag(); return DefaultAshBlossomAndJoyousSpring(); } @@ -1658,7 +1649,6 @@ public bool AshBlossom_JoyousSpringActivate() public bool PSYGammaActivate() { if (NegatedCheck(true)) return false; - CheckDeactiveFlag(); return true; } @@ -1683,7 +1673,7 @@ public bool PotterieActivate() return true; } } - + // safe check if (CheckProblematicCards() == null){ int[] checklist = {CardId.Patronus, CardId.GolemAruru}; @@ -1775,7 +1765,7 @@ public bool LightningStormActivate() if (opt == -1) return false; // only one selection - if (Enemy.MonsterZone.GetFirstMatchingCard(card => card.IsAttack()) == null + if (Enemy.MonsterZone.GetFirstMatchingCard(card => card.IsAttack()) == null || Enemy.GetSpellCount() == 0) { AI.SelectOption(0); @@ -1930,30 +1920,30 @@ public bool CalledbytheGraveActivate() // negate if (Util.GetLastChainCard().IsMonster()) { - int code = Util.GetLastChainCard().Id; + int code = Util.GetLastChainCard().GetOriginCode(); if (code == 0) return false; - if (CheckCalledbytheGrave(code) > 0 || CrossoutDesignatorTarget == code) return false; - if (Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.IsOriginalCode(code)) != null) + if (CheckCalledbytheGrave(code) > 0) return false; + ClientCard target = Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.IsOriginalCode(code)); + if (target != null) { if (!(Card.Location == CardLocation.SpellZone)) { SelectSTPlace(null, true); } - AI.SelectCard(code); - CalledbytheGraveCount[code] = 2; - CheckDeactiveFlag(); + AI.SelectCard(target); + currentNegatingIdList.Add(code); return true; } } - + // banish target foreach (ClientCard cards in Enemy.Graveyard) { if (Duel.ChainTargets.Contains(cards)) { - int code = cards.Id; + int code = cards.GetOriginCode(); AI.SelectCard(cards); - CalledbytheGraveCount[code] = 2; + currentNegatingIdList.Add(code); return true; } } @@ -1966,9 +1956,9 @@ public bool CalledbytheGraveActivate() { enemy_monsters.Sort(CardContainer.CompareCardAttack); enemy_monsters.Reverse(); - int code = enemy_monsters[0].Id; - AI.SelectCard(code); - CalledbytheGraveCount[code] = 2; + int code = enemy_monsters[0].GetOriginCode(); + AI.SelectCard(enemy_monsters); + currentNegatingIdList.Add(code); return true; } } @@ -1978,13 +1968,13 @@ public bool CalledbytheGraveActivate() if (Duel.LastChainPlayer == 1) return false; List targets = CheckDangerousCardinEnemyGrave(true); if (targets.Count() > 0) { - int code = targets[0].Id; + int code = targets[0].GetOriginCode(); if (!(Card.Location == CardLocation.SpellZone)) { SelectSTPlace(null, true); } - AI.SelectCard(code); - CalledbytheGraveCount[code] = 2; + AI.SelectCard(targets); + currentNegatingIdList.Add(code); return true; } @@ -2024,14 +2014,12 @@ public bool DrapingActivate() public bool CrossoutDesignatorActivate() { if (NegatedCheck(true) || CheckLastChainNegated()) return false; - // negate + // negate if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard() != null) { - int code = Util.GetLastChainCard().Id; - int alias = Util.GetLastChainCard().Alias; - if (alias != 0 && alias - code < 10) code = alias; + int code = Util.GetLastChainCard().GetOriginCode(); if (code == 0) return false; - if (CheckCalledbytheGrave(code) > 0 || CrossoutDesignatorTarget == code) return false; + if (CheckCalledbytheGrave(code) > 0) return false; if (CheckRemainInDeck(code) > 0) { if (!(Card.Location == CardLocation.SpellZone)) @@ -2039,8 +2027,7 @@ public bool CrossoutDesignatorActivate() SelectSTPlace(null, true); } AI.SelectAnnounceID(code); - CrossoutDesignatorTarget = code; - CheckDeactiveFlag(); + currentNegatingIdList.Add(code); return true; } } @@ -2054,7 +2041,7 @@ public bool UnveilingActivate() if (NegatedCheck(true)) return false; // LightningStorm check - if (Bot.HasInHandOrInSpellZone(CardId.LightningStorm)) + if (Bot.HasInHandOrInSpellZone(_CardId.LightningStorm)) { int faceup_count = Bot.SpellZone.GetMatchingCardsCount(card => card.IsFaceup()); faceup_count += Bot.MonsterZone.GetMatchingCardsCount(card => card.IsFaceup()); @@ -2152,15 +2139,14 @@ public bool WitchcrafterBystreetActivate() public bool InfiniteImpermanenceActivate() { if (SpellNegatable()) return false; - if (CrossoutDesignatorTarget == CardId.InfiniteImpermanence) return false; if (CheckLastChainNegated()) return false; // negate before monster's effect's used foreach (ClientCard m in Enemy.GetMonsters()) { - if (!m.IsDisabled() && Duel.LastChainPlayer != 0 && + if (!m.IsDisabled() && Duel.LastChainPlayer != 0 && ((m.IsMonsterShouldBeDisabledBeforeItUseEffect() || m.IsFloodgate()) - || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2 && - (m.IsMonsterDangerous() || m.IsMonsterInvincible() + || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2 && + (m.IsMonsterDangerous() || m.IsMonsterInvincible() || (m.IsMonsterHasPreventActivationEffectInBattle() && Bot.HasInMonstersZone(CardId.MadameVerre))) ))) { @@ -2273,7 +2259,7 @@ public bool MasterpieceActivate() // ss effect { // LightningStorm check - if (Bot.HasInHandOrInSpellZone(CardId.LightningStorm)) + if (Bot.HasInHandOrInSpellZone(_CardId.LightningStorm)) { int faceup_count = Bot.SpellZone.GetMatchingCardsCount(card => card.IsFaceup()); faceup_count += Bot.MonsterZone.GetMatchingCardsCount(card => card.IsFaceup()); @@ -2460,7 +2446,6 @@ public bool BorreloadSavageDragonActivate() // negate if (NegatedCheck(true) || Duel.LastChainPlayer != 1) return false; if (Util.GetLastChainCard().HasSetcode(0x11e) && Util.GetLastChainCard().Location == CardLocation.Hand) return false; - CheckDeactiveFlag(); return false; } @@ -2512,10 +2497,10 @@ public bool PSYOmegaActivate() } if (CheckProblematicCards() == null) { - AI.SelectCard(CardId.CalledbytheGrave, CardId.CrossoutDesignator, - CardId.MaxxC, CardId.AshBlossom_JoyousSpring, - CardId.MagicianRightHand, CardId.MagiciansLeftHand, CardId.MagiciansRestage, CardId.Patronus, - CardId.LightningStorm, CardId.Reasoning); + AI.SelectCard(_CardId.CalledByTheGrave, _CardId.CrossoutDesignator, + _CardId.MaxxC, _CardId.AshBlossom, + CardId.MagicianRightHand, CardId.MagiciansLeftHand, CardId.MagiciansRestage, CardId.Patronus, + _CardId.LightningStorm, CardId.Reasoning); return true; } } @@ -2666,7 +2651,7 @@ public bool KnightmareUnicornActivate() } return false; } - + // check whether summon KnightmarePhoenix public List KnightmarePhoenixSummonCheck(ClientCard included = null) { @@ -2735,7 +2720,7 @@ public List CrystronHalqifibraxSummonCheck(ClientCard included = nul if (materials.Count < 2) return empty_list; // need CrystronHalqifibrax? - if (CheckRemainInDeck(CardId.PSYGamma, CardId.AshBlossom_JoyousSpring) == 0) return empty_list; + if (CheckRemainInDeck(CardId.PSYGamma, _CardId.AshBlossom) == 0) return empty_list; return empty_list; @@ -2914,5 +2899,15 @@ public bool ChickenGame() } return false; } + + protected override bool DefaultSetForDiabellze() + { + if (base.DefaultSetForDiabellze()) + { + SelectSTPlace(null, true); + return true; + } + return false; + } } } diff --git a/Game/AI/Decks/YosenjuExecutor.cs b/Game/AI/Decks/YosenjuExecutor.cs index 8e603c53..424bd10f 100644 --- a/Game/AI/Decks/YosenjuExecutor.cs +++ b/Game/AI/Decks/YosenjuExecutor.cs @@ -155,6 +155,7 @@ public override bool OnSelectHand() public override void OnNewTurn() { CardOfDemiseUsed = false; + base.OnNewTurn(); } public override bool OnSelectYesNo(long desc) diff --git a/Game/AI/Decks/YubelExecutor.cs b/Game/AI/Decks/YubelExecutor.cs new file mode 100644 index 00000000..8f88b2d5 --- /dev/null +++ b/Game/AI/Decks/YubelExecutor.cs @@ -0,0 +1,2359 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +using YGOSharp.Network.Enums; +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; + +namespace WindBot.Game.AI.Decks +{ + [Deck("Yubel", "AI_Yubel")] + public class YubelExecutor : DefaultExecutor + { + public class CardId + { + // === Core Yubel === + public const int YUBEL = 78371393; + public const int YUBEL_TERROR_INCARNATE = 4779091; + public const int SPIRIT_OF_YUBEL = 90829280; + public const int PHANTOM_OF_YUBEL = 80453041; // (extra) + + // === Starters / Engine === + public const int SAMSARA_D_LOTUS = 62318994; + public const int GRUESOME_GRAVE_SQUIRMER = 24215921; + public const int FABLED_LURRIE = 97651498; + + // === Fiendsmith / Fiend package === + public const int SHARVARA = 41165831; + public const int FIENDSMITH_ENGRAVER = 60764609; + public const int LACRIMA_CT = 28803166; + public const int DARK_BECKONING_BEAST = 81034083; + public const int CHAOS_SUMMONING_BEAST = 27439792; + + // === Spells / Traps === + public const int NIGHTMARE_PAIN = 65261141; + public const int NIGHTMARE_THRONE = 93729896; + public const int SPIRIT_GATES = 80312545; + public const int FIENDSMITH_TRACT = 98567237; + public const int ABOMINABLE_CHAMBER = 80801743; + public const int FIENDSMITHS_PARADISE = 99989863; + public const int TERRAFORMING = 73628505; + + // === Extra Deck / อื่น ๆ === + public const int FIENDSMITHS_DESIRAE = 82135803; + //public const int FIENDSMITHS_LACRIMA = 46640168; + public const int VARUDASN_FINAL_BRINGER = 70636044; + public const int DDD_WAVE_HIGH_KING_CAESAR = 79559912; + public const int UNCHAINED_LORD_OF_YAMA = 24269961; + public const int UNCHAINED_SOUL_OF_RAGE = 67680512; + public const int SP_LITTLE_KNIGHT = 29301450; + public const int MOON_OF_THE_CLOSED_HEAVEN = 71818935; + //public const int FIENDSMITHS_SEQUENCE = 49867899; + //public const int MUCKRAKER_UNDERWORLD = 71607202; + public const int FIENDSMITHS_REQUIEM = 2463794; + public const int SALAMANGREAT_ALMIRAJ = 60303245; + public const int NECROQUIP = 93860227; + //public const int ARIALEATER = 28143384; + public const int GORGONOFZIL = 12067160; + public const int GUSTAVMAX = 56910167; + public const int JUGGERNAUT = 26096328; + public const int UNCHAINDEDABOMINATION = 29479265; + + // === Handtraps / Others (blacklist/targets etc.) === + public const int Fuwalos = 42141493; + public const int NaturalExterio = 99916754; + public const int NaturalBeast = 33198837; + public const int ImperialOrder = 61740673; + public const int SwordsmanLV7 = 37267041; + public const int RoyalDecree = 51452091; + public const int Number41BagooskatheTerriblyTiredTapir = 90590303; + public const int InspectorBoarder = 15397015; + public const int SkillDrain = 82732705; + public const int DivineArsenalAAZEUS_SkyThunder = 90448279; + public const int DimensionShifter = 91800273; + public const int MacroCosmos = 30241314; + public const int DimensionalFissure = 81674782; + public const int BanisheroftheRadiance = 94853057; + public const int BanisheroftheLight = 61528025; + public const int KashtiraAriseHeart = 48626373; + public const int AccesscodeTalker = 86066372; + public const int GhostMournerMoonlitChill = 52038441; + public const int NibiruThePrimalBeing = 27204311; + } + + const int SetcodeTimeLord = 0x4a; + const int SetcodePhantom = 0xdb; + const int SetcodeOrcust = 0x11b; + const int SetcodeHorus = 0x19d; + + Dictionary> DeckCountTable = new Dictionary>{ + {3, new List { CardId.SPIRIT_OF_YUBEL, CardId.SAMSARA_D_LOTUS,CardId.NIGHTMARE_THRONE, CardId.SPIRIT_GATES, + _CardId.AshBlossom, CardId.DARK_BECKONING_BEAST,_CardId.InfiniteImpermanence } }, + {2, new List { _CardId.MaxxC, _CardId.CalledByTheGrave,CardId.GRUESOME_GRAVE_SQUIRMER}}, + {1, new List { CardId.FIENDSMITH_ENGRAVER, CardId.FIENDSMITHS_PARADISE, + CardId.YUBEL, CardId.YUBEL_TERROR_INCARNATE, CardId.ABOMINABLE_CHAMBER, + _CardId.CrossoutDesignator, CardId.GRUESOME_GRAVE_SQUIRMER, CardId.FABLED_LURRIE, + CardId.NIGHTMARE_PAIN, CardId.TERRAFORMING, CardId.FIENDSMITH_TRACT,CardId.SHARVARA, + CardId.CHAOS_SUMMONING_BEAST, CardId.LACRIMA_CT + } } + }; + + List notToNegateIdList = new List { 58699500, 20343502, 19403423 }; + List notToDestroySpellTrap = new List { 50005218, 6767771 }; + List targetNegateIdList = new List { + _CardId.EffectVeiler, _CardId.InfiniteImpermanence, CardId.GhostMournerMoonlitChill, _CardId.BreakthroughSkill, 74003290, 67037924, + 9753964, 66192538, 23204029, 73445448, 35103106, 30286474, 45002991, 5795980, 38511382, 53742162, 30430448 + }; + + private static readonly int[] LinkFodderPriority = new[] + { + CardId.SHARVARA, + CardId.YUBEL_TERROR_INCARNATE, + CardId.UNCHAINED_SOUL_OF_RAGE, + CardId.UNCHAINED_LORD_OF_YAMA, + CardId.GRUESOME_GRAVE_SQUIRMER, + CardId.FABLED_LURRIE, + CardId.LACRIMA_CT, + CardId.SALAMANGREAT_ALMIRAJ, + CardId.DARK_BECKONING_BEAST, + CardId.CHAOS_SUMMONING_BEAST, + CardId.SAMSARA_D_LOTUS, + CardId.SPIRIT_OF_YUBEL, + CardId.YUBEL, + CardId.FIENDSMITH_ENGRAVER, + }; + + private static readonly HashSet YUBEL_SET = new HashSet { CardId.YUBEL, CardId.YUBEL_TERROR_INCARNATE, CardId.SPIRIT_OF_YUBEL, CardId.PHANTOM_OF_YUBEL }; + + public YubelExecutor(GameAI ai, Duel duel) : base(ai, duel) + { + + AddExecutor(ExecutorType.Activate, CardId.NIGHTMARE_THRONE, ActNightmareThroneSearch); + // ===== Generic counters ===== + AddExecutor(ExecutorType.Activate, _CardId.CalledByTheGrave, CalledbytheGraveActivate); + AddExecutor(ExecutorType.Activate, _CardId.CrossoutDesignator, CrossoutDesignatorActivate); + AddExecutor(ExecutorType.Activate, _CardId.InfiniteImpermanence, InfiniteImpermanenceActivate); + AddExecutor(ExecutorType.Activate, _CardId.AshBlossom, AshBlossomActivate); + AddExecutor(ExecutorType.Activate, _CardId.MaxxC, MaxxCActivate); + + // ===== Yubel-related ===== + AddExecutor(ExecutorType.Activate, CardId.SAMSARA_D_LOTUS, ActSamsaraDLotusGY); + AddExecutor(ExecutorType.Activate, CardId.YUBEL); + AddExecutor(ExecutorType.Activate, CardId.UNCHAINDEDABOMINATION, UnchainedAbominationActivate); + AddExecutor(ExecutorType.Activate, CardId.PHANTOM_OF_YUBEL, DontSelfNG); + + + // ===== Engine pieces ===== + AddExecutor(ExecutorType.Activate, CardId.ABOMINABLE_CHAMBER, ActAbo); + //AddExecutor(ExecutorType.Activate, CardId.SP_LITTLE_KNIGHT, ActLittleKnightSM); + AddExecutor(ExecutorType.Activate, CardId.SP_LITTLE_KNIGHT, ActLittleKnight); + AddExecutor(ExecutorType.Activate, CardId.DDD_WAVE_HIGH_KING_CAESAR, DontSelfNG); + AddExecutor(ExecutorType.Activate, CardId.FIENDSMITHS_DESIRAE, ActDesirae); + AddExecutor(ExecutorType.Activate, CardId.VARUDASN_FINAL_BRINGER, ActVarudras); + AddExecutor(ExecutorType.Activate, CardId.UNCHAINED_SOUL_OF_RAGE, ActRageQuickLink); + AddExecutor(ExecutorType.Activate, CardId.FIENDSMITHS_PARADISE, ActParadise); + + AddExecutor(ExecutorType.Activate, CardId.FIENDSMITH_ENGRAVER, ActEngraverHand); + AddExecutor(ExecutorType.Activate, CardId.FIENDSMITH_TRACT, ActTract); + AddExecutor(ExecutorType.SpSummon, CardId.FABLED_LURRIE); + AddExecutor(ExecutorType.SpSummon, CardId.FIENDSMITHS_REQUIEM, SSRequiem); + AddExecutor(ExecutorType.Activate, CardId.FIENDSMITHS_REQUIEM, ActRequiemMZ); + AddExecutor(ExecutorType.Activate, CardId.LACRIMA_CT, ActLacimaCT); + AddExecutor(ExecutorType.Activate, CardId.FIENDSMITHS_REQUIEM, ActRequiemEQ); + AddExecutor(ExecutorType.SpSummon, CardId.NECROQUIP, SSNecroquip); + AddExecutor(ExecutorType.Activate, CardId.FIENDSMITH_ENGRAVER, ActEngraverGY); + AddExecutor(ExecutorType.SpSummon, CardId.DDD_WAVE_HIGH_KING_CAESAR); + + AddExecutor(ExecutorType.Activate, CardId.LACRIMA_CT, ActLacimaCTGY); + + // Field & search line + AddExecutor(ExecutorType.Activate, S1_ActivateTerraformingForThrone); + AddExecutor(ExecutorType.Activate, CardId.NIGHTMARE_THRONE, S6_ChainThroneFollowUp); + + // Normal Summon engine + AddExecutor(ExecutorType.Summon, CardId.DARK_BECKONING_BEAST, NSDarkBeckoningBeast); + AddExecutor(ExecutorType.Activate, CardId.DARK_BECKONING_BEAST, ActDarkBeckoningBeast); + + // === SPIRIT GATES === + AddExecutor(ExecutorType.Activate, CardId.SPIRIT_GATES, S4_ActivateSpiritGates); // ใช้จากมือเพื่อค้น + AddExecutor(ExecutorType.Activate, CardId.SPIRIT_GATES, Gate_RecycleContinuous); // e3: เก็บ Continuous (มีเลเวล 10) + + + AddExecutor(ExecutorType.Summon, CardId.SAMSARA_D_LOTUS, NSSamsaraDLotus); + AddExecutor(ExecutorType.Activate, CardId.SAMSARA_D_LOTUS, ActSamsaraDLotus); + + // Follow-ups + AddExecutor(ExecutorType.SpSummon, CardId.SPIRIT_OF_YUBEL); + AddExecutor(ExecutorType.Activate, CardId.SPIRIT_OF_YUBEL); + AddExecutor(ExecutorType.Activate, CardId.NIGHTMARE_PAIN, ActNightmarePainHand); + AddExecutor(ExecutorType.Activate, CardId.NIGHTMARE_PAIN, ActNightmarePainEffect); + AddExecutor(ExecutorType.Activate, CardId.GRUESOME_GRAVE_SQUIRMER, SSGGS); + AddExecutor(ExecutorType.Activate, CardId.GRUESOME_GRAVE_SQUIRMER, ActGGSGY); + + AddExecutor(ExecutorType.SpSummon, CardId.MOON_OF_THE_CLOSED_HEAVEN, SSMoon); + AddExecutor(ExecutorType.SpSummon, CardId.UNCHAINED_LORD_OF_YAMA, L2YamaSetup); + AddExecutor(ExecutorType.Activate, CardId.UNCHAINED_LORD_OF_YAMA, ActYamaMZ); + AddExecutor(ExecutorType.Activate, CardId.UNCHAINED_LORD_OF_YAMA, ActYamaGY); + AddExecutor(ExecutorType.SpSummon, CardId.UNCHAINED_SOUL_OF_RAGE, L2RageKeepYama); + AddExecutor(ExecutorType.SpSummon, CardId.UNCHAINDEDABOMINATION, L4ABOSS); + AddExecutor(ExecutorType.Activate, CardId.SHARVARA, ActSharvara); + AddExecutor(ExecutorType.Activate, CardId.SHARVARA, ActSharvaraGY); + //AddExecutor(ExecutorType.SpSummon, CardId.MUCKRAKER_UNDERWORLD, L2NoBrain); + + + + // Panic line + //AddExecutor(ExecutorType.SpSummon, CardId.VARUDASN_FINAL_BRINGER, SSVarudras); + AddExecutor(ExecutorType.SpSummon, CardId.VARUDASN_FINAL_BRINGER); + AddExecutor(ExecutorType.SpSummon, CardId.SALAMANGREAT_ALMIRAJ, AlmirajSummon); + AddExecutor(ExecutorType.Activate, CardId.SPIRIT_GATES, Gate_Revive00Fiend); // e2: ทิ้งมือ ชุบ Fiend 0/0 + AddExecutor(ExecutorType.SpSummon, CardId.PHANTOM_OF_YUBEL, SSPhantom); + AddExecutor(ExecutorType.SpSummon, CardId.GUSTAVMAX); + AddExecutor(ExecutorType.Activate, CardId.GUSTAVMAX); + AddExecutor(ExecutorType.SpSummon, CardId.JUGGERNAUT); + AddExecutor(ExecutorType.Activate, CardId.JUGGERNAUT); + //AddExecutor(ExecutorType.Activate, CardId.MUCKRAKER_UNDERWORLD, NeverUseMuckrakerRevive); + + // other + AddExecutor(ExecutorType.SpellSet, SpellSetCheck); + AddExecutor(ExecutorType.Repos, MonsterRepos); + } + //======================Default code + #region Default Code Start Here + private int _totalAttack; + private int _totalBotAttack; + bool enemyActivateMaxxC = false; + bool enemyActivateLockBird = false; + int dimensionShifterCount = 0; + bool enemyActivateInfiniteImpermanenceFromHand = false; + List infiniteImpermanenceList = new List(); + List currentNegateCardList = new List(); + List currentDestroyCardList = new List(); + List sendToGYThisTurn = new List(); + List activatedCardIdList = new List(); + List enemyPlaceThisTurn = new List(); + List escapeTargetList = new List(); + List summonThisTurn = new List(); + + private enum ThroneStage { None, Searching, AwaitDestroyPrompt } + private bool _yubelWantsTribute = false; + private ThroneStage _throneStage = ThroneStage.None; + // === Spirit Gates state === + int _gateReviveTargetId = 0; // จะชุบตัวไหน + int _gateDiscardPreferredId = 0; // จะทิ้งใบไหนเป็น cost + bool _gateWantsRecycle = false; // กำลังจะกดโหมดเก็บ Continuous + bool moonSummoned = false; + bool requiemSummoned = false; + bool thronePending = false; // we're in a Throne activation flow + bool throneSearched = false; // after we chose the monster to search + int throneDesiredPick = 0; // preferred monster id to search + + public List ShuffleList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(result.Count); + int nextIndex = (index + Program.Rand.Next(result.Count - 1)) % result.Count; + T tempCard = result[index]; + result[index] = result[nextIndex]; + result[nextIndex] = tempCard; + } + return result; + } + + public override bool OnSelectHand() { return true; } + + public List ShuffleCardList(List list) + { + List result = list; + int n = result.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(n + 1); + ClientCard temp = result[index]; + result[index] = result[n]; + result[n] = temp; + } + return result; + } + + public int CheckRemainInDeck(int id) + { + for (int count = 1; count < 4; ++count) + { + if (DeckCountTable[count].Contains(id)) + { + return Bot.GetRemainingCount(id, count); + } + } + return 0; + } + + private bool MonsterRepos() + { + bool isYubelFam = + Card.Id == CardId.SPIRIT_OF_YUBEL || + Card.Id == CardId.YUBEL || + Card.Id == CardId.YUBEL_TERROR_INCARNATE || + Card.Id == CardId.PHANTOM_OF_YUBEL; + + if (isYubelFam) + { + if (Card.IsDefense()) + { + AI.SelectPosition(CardPosition.Attack); + return true; + } + return false; + } + if (Card.IsFacedown()) + return true; + if (CheckInDanger() && (_totalAttack > _totalBotAttack)) + return Card.IsDefense(); + return DefaultMonsterRepos(); + } + + public bool CheckAtAdvantage() + { + if (GetProblematicEnemyMonster() == null && Bot.GetMonsters().Any(card => card.IsFaceup())) + { + return true; + } + return false; + } + + public bool CheckInDanger() + { + if (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2) + { + int totalAtk = 0; + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsAttack() && !m.Attacked) totalAtk += m.Attack; + } + if (totalAtk >= Bot.LifePoints) return true; + } + return false; + } + + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + if (player == 0 && location == CardLocation.MonsterZone && cardId == CardId.UNCHAINED_SOUL_OF_RAGE) + { + int prefer = (GetMyLinkedMMZMask() & available) & 0x1F; + + int choose = (prefer != 0) ? LowestBit(prefer) + : LowestBit(available & 0x1F); // fallback + + AI.SelectPlace(choose); + return choose; + } + SelectSTPlace(Card, true); + return base.OnSelectPlace(cardId, player, location, available); + } + + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + if (positions == null || positions.Count == 0) + return base.OnSelectPosition(cardId, positions); + + bool isYubelFamily = + YUBEL_SET.Contains(cardId) || + (Card != null && YUBEL_SET.Contains(Card.Id)) || + (Card != null && (Card.Name?.Contains("Yubel") ?? false)); + + if(!isYubelFamily) + return base.OnSelectPosition(cardId, positions); + + CardPosition atkPref = + positions.Contains(CardPosition.FaceUpAttack) ? CardPosition.FaceUpAttack : + positions.Contains(CardPosition.Attack) ? CardPosition.Attack : + (CardPosition)0; + + if (isYubelFamily && atkPref != 0) + { + AI.SelectPosition(atkPref); + return atkPref; + } + + CardPosition chosen = positions[0]; + AI.SelectPosition(chosen); + return chosen; + } + + public bool AshBlossomActivate() + { + if (BlockIfThrone("AshBlossom")) return false;//added + if (InThroneFlow) return false;//added + if (CheckWhetherNegated(true) || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + if (CheckAtAdvantage()) + { + return false; + } + } + return DefaultAshBlossomAndJoyousSpring(); + } + + public bool MaxxCActivate() + { + if (BlockIfThrone("MaxxC")) return false; + if (InThroneFlow) return false;//added + if (CheckWhetherNegated(true) || Duel.LastChainPlayer == 0) return false; + return DefaultMaxxC(); + } + + public bool InfiniteImpermanenceActivate() + { + if (CheckWhetherNegated()) return false; + foreach (ClientCard m in Enemy.GetMonsters()) + { + if (m.IsMonsterShouldBeDisabledBeforeItUseEffect() && !m.IsDisabled() && Duel.LastChainPlayer != 0) + { + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + AI.SelectCard(m); + return true; + } + } + + ClientCard LastChainCard = Util.GetLastChainCard(); + + if (Card.Location == CardLocation.SpellZone) + { + int this_seq = -1; + int that_seq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) this_seq = i; + if (LastChainCard != null + && LastChainCard.Controller == 1 && LastChainCard.Location == CardLocation.SpellZone && Enemy.SpellZone[i] == LastChainCard) that_seq = i; + else if (Duel.Player == 0 && Util.GetProblematicEnemySpell() != null + && Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFloodgate()) that_seq = i; + } + if ((this_seq * that_seq >= 0 && this_seq + that_seq == 4) + || (Util.IsChainTarget(Card)) + || (LastChainCard != null && LastChainCard.Controller == 1 && LastChainCard.IsCode(_CardId.HarpiesFeatherDuster))) + { + ClientCard target = GetProblematicEnemyMonster(canBeTarget: true); + List enemyMonsters = Enemy.GetMonsters(); + AI.SelectCard(target); + infiniteImpermanenceList.Add(this_seq); + return true; + } + } + if ((LastChainCard == null || LastChainCard.Controller != 1 || LastChainCard.Location != CardLocation.MonsterZone + || LastChainCard.IsDisabled() || LastChainCard.IsShouldNotBeTarget() || LastChainCard.IsShouldNotBeSpellTrapTarget())) + return false; + + if (Card.Location == CardLocation.SpellZone) + { + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) + { + infiniteImpermanenceList.Add(i); + break; + } + } + } + if (Card.Location == CardLocation.Hand) + { + SelectSTPlace(Card, true); + } + if (LastChainCard != null) AI.SelectCard(LastChainCard); + else + { + List enemyMonsters = Enemy.GetMonsters(); + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + foreach (ClientCard card in enemyMonsters) + { + if (card.IsFaceup() && !card.IsShouldNotBeTarget() && !card.IsShouldNotBeSpellTrapTarget()) + { + AI.SelectCard(card); + return true; + } + } + } + return true; + } + + public bool CrossoutDesignatorActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (Duel.LastChainPlayer == 1 && Util.GetLastChainCard() != null) + { + int code = Util.GetLastChainCard().Id; + int alias = Util.GetLastChainCard().Alias; + if (alias != 0 && alias - code < 10) code = alias; + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (CheckRemainInDeck(code) > 0) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectAnnounceID(code); + currentNegateCardList.AddRange(Enemy.MonsterZone.Where(c => c != null && c.IsFaceup() && c.IsCode(code))); + return true; + } + } + return false; + } + + public bool CalledbytheGraveActivate() + { + if (CheckWhetherNegated() || !CheckLastChainShouldNegated()) return false; + if (CheckAtAdvantage() && Duel.LastChainPlayer == 1 && Util.GetLastChainCard().IsCode(_CardId.MaxxC)) + { + return false; + } + if (Duel.LastChainPlayer == 1) + { + if (Util.GetLastChainCard().IsMonster()) + { + int code = Util.GetLastChainCard().GetOriginCode(); + if (code == 0) return false; + if (DefaultCheckWhetherCardIdIsNegated(code)) return false; + if (Util.GetLastChainCard().IsCode(_CardId.MaxxC) && CheckAtAdvantage()) + { + return false; + } + ClientCard graveTarget = Enemy.Graveyard.GetFirstMatchingCard(card => card.IsMonster() && card.GetOriginCode() == code); + if (graveTarget != null) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(graveTarget); + currentDestroyCardList.Add(graveTarget); + return true; + } + } + + foreach (ClientCard graveCard in Enemy.Graveyard) + { + if (Duel.ChainTargets.Contains(graveCard) && graveCard.IsMonster()) + { + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + int code = graveCard.Id; + AI.SelectCard(graveCard); + currentDestroyCardList.Add(graveCard); + return true; + } + } + + if (Duel.ChainTargets.Contains(Card)) + { + List enemyMonsters = Enemy.Graveyard.GetMatchingCards(card => card.IsMonster()).ToList(); + if (enemyMonsters.Count > 0) + { + enemyMonsters.Sort(CardContainer.CompareCardAttack); + enemyMonsters.Reverse(); + int code = enemyMonsters[0].Id; + AI.SelectCard(code); + currentDestroyCardList.Add(enemyMonsters[0]); + return true; + } + } + } + + if (Duel.LastChainPlayer == 1) return false; + List targets = GetDangerousCardinEnemyGrave(true); + if (targets.Count > 0) + { + int code = targets[0].Id; + if (!(Card.Location == CardLocation.SpellZone)) + { + SelectSTPlace(null, true); + } + AI.SelectCard(code); + currentDestroyCardList.Add(targets[0]); + return true; + } + + return false; + } + + public bool SpellSetCheck() + { + if (Card.Id == CardId.FIENDSMITHS_PARADISE) return false; + + if (Duel.Phase == DuelPhase.Main1 && Bot.HasAttackingMonster() && Duel.Turn > 1) return false; + List onlyOneSetList = new List { CardId.ABOMINABLE_CHAMBER }; + if (onlyOneSetList.Contains(Card.Id) && Bot.HasInSpellZone(Card.Id)) + { + return false; + } + + if ((Card.IsTrap() || Card.HasType(CardType.QuickPlay)))//added + { + List avoid_list = new List(); + int setFornfiniteImpermanence = 0; + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] != null && Enemy.SpellZone[i].IsFaceup() && Bot.SpellZone[4 - i] == null) + { + avoid_list.Add(4 - i); + setFornfiniteImpermanence += (int)System.Math.Pow(2, 4 - i); + } + } + if (Bot.HasInHand(_CardId.InfiniteImpermanence)) + { + if (Card.IsCode(_CardId.InfiniteImpermanence)) + { + AI.SelectPlace(setFornfiniteImpermanence); + return true; + } + else + { + SelectSTPlace(Card, false, avoid_list); + return true; + } + } + else + { + SelectSTPlace(); + } + return true; + } + + return false; + } + + public List GetDangerousCardinEnemyGrave(bool onlyMonster = false) + { + List result = Enemy.Graveyard.GetMatchingCards(card => + (!onlyMonster || card.IsMonster()) && (card.HasSetcode(SetcodeOrcust) || card.HasSetcode(SetcodePhantom) || card.HasSetcode(SetcodeHorus))).ToList(); + List dangerMonsterIdList = new List { 99937011, 63542003, 9411399, 28954097, 30680659 }; + result.AddRange(Enemy.Graveyard.GetMatchingCards(card => dangerMonsterIdList.Contains(card.Id))); + return result; + } + + public bool CheckWhetherNegated(bool disablecheck = true, bool toFieldCheck = false, CardType type = 0) + { + bool isMonster = type == 0 && Card.IsMonster(); + isMonster |= ((int)type & (int)CardType.Monster) != 0; + bool isSpellOrTrap = type == 0 && (Card.IsSpell() || Card.IsTrap()); + isSpellOrTrap |= (((int)type & (int)CardType.Spell) != 0) || (((int)type & (int)CardType.Trap) != 0); + bool isCounter = ((int)type & (int)CardType.Counter) != 0; + if (isSpellOrTrap && toFieldCheck && CheckSpellWillBeNegate(isCounter)) + return true; + if (DefaultCheckWhetherCardIsNegated(Card)) return true; + if (isMonster && (toFieldCheck || Card.Location == CardLocation.MonsterZone)) + { + if ((toFieldCheck && (((int)type & (int)CardType.Link) != 0)) || Card.IsDefense()) + { + if (Enemy.MonsterZone.Any(card => CheckNumber41(card)) || Bot.MonsterZone.Any(card => CheckNumber41(card))) return true; + } + if (Enemy.HasInSpellZone(CardId.SkillDrain, true)) return true; + } + if (disablecheck) return (Card.Location == CardLocation.MonsterZone || Card.Location == CardLocation.SpellZone) && Card.IsDisabled() && Card.IsFaceup(); + return false; + } + + public bool CheckNumber41(ClientCard card) + { + return card != null && card.IsFaceup() && card.IsCode(CardId.Number41BagooskatheTerriblyTiredTapir) && card.IsDefense() && !card.IsDisabled(); + } + + public void SelectSTPlace(ClientCard card = null, bool avoidImpermanence = false, List avoidList = null) + { + if (card == null) card = Card; + List list = new List(); + for (int seq = 0; seq < 5; ++seq) + { + if (Bot.SpellZone[seq] == null) + { + if (card != null && card.Location == CardLocation.Hand && avoidImpermanence && infiniteImpermanenceList.Contains(seq)) continue; + if (avoidList != null && avoidList.Contains(seq)) continue; + list.Add(seq); + } + } + int n = list.Count; + while (n-- > 1) + { + int index = Program.Rand.Next(list.Count); + int nextIndex = (index + Program.Rand.Next(list.Count - 1)) % list.Count; + int tempInt = list[index]; + list[index] = list[nextIndex]; + list[nextIndex] = tempInt; + } + if (avoidImpermanence && Bot.GetMonsters().Any(c => c.IsFaceup() && !c.IsDisabled())) + { + foreach (int seq in list) + { + ClientCard enemySpell = Enemy.SpellZone[4 - seq]; + if (enemySpell != null && enemySpell.IsFacedown()) continue; + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + } + foreach (int seq in list) + { + int zone = (int)System.Math.Pow(2, seq); + AI.SelectPlace(zone); + return; + } + AI.SelectPlace(0); + } + + public bool CheckSpellWillBeNegate(bool isCounter = false, ClientCard target = null) + { + if (target == null) target = Card; + if (target.Location != CardLocation.SpellZone && target.Location != CardLocation.Hand) return false; + + if (Enemy.HasInMonstersZone(CardId.NaturalExterio, true) && !isCounter) return true; + if (target.IsSpell()) + { + if (Enemy.HasInMonstersZone(CardId.NaturalBeast, true)) return true; + if (Enemy.HasInSpellZone(CardId.ImperialOrder, true) || Bot.HasInSpellZone(CardId.ImperialOrder, true)) return true; + if (Enemy.HasInMonstersZone(CardId.SwordsmanLV7, true) || Bot.HasInMonstersZone(CardId.SwordsmanLV7, true)) return true; + } + if (target.IsTrap() && (Enemy.HasInSpellZone(CardId.RoyalDecree, true) || Bot.HasInSpellZone(CardId.RoyalDecree, true))) return true; + if (target.Location == CardLocation.SpellZone && (target.IsSpell() || target.IsTrap())) + { + int selfSeq = -1; + for (int i = 0; i < 5; ++i) + { + if (Bot.SpellZone[i] == Card) selfSeq = i; + } + if (infiniteImpermanenceList.Contains(selfSeq)) return true; + } + return false; + } + + public bool CheckLastChainShouldNegated() + { + ClientCard lastcard = Util.GetLastChainCard(); + if (lastcard == null || lastcard.Controller != 1) return false; + if (lastcard.IsMonster() && lastcard.HasSetcode(SetcodeTimeLord) && Duel.Phase == DuelPhase.Standby) return false; + if (notToNegateIdList.Contains(lastcard.Id)) return false; + if (DefaultCheckWhetherCardIsNegated(lastcard)) return false; + if (Duel.Turn == 1 && lastcard.IsCode(_CardId.MaxxC)) return false; + + return true; + } + + public ClientCard GetProblematicEnemyMonster(int attack = 0, bool canBeTarget = false, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + ClientCard floodagateCard = Enemy.GetMonsters().Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsFloodgate() && c.IsFaceup() + && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (floodagateCard != null) return floodagateCard; + + ClientCard dangerCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (dangerCard != null) return dangerCard; + + ClientCard invincibleCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (invincibleCard != null) return invincibleCard; + + ClientCard equippedCard = Enemy.MonsterZone.Where(c => c?.Data != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && c.EquipCards.Count > 0 && CheckCanBeTargeted(c, canBeTarget, selfType) + && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (equippedCard != null) return equippedCard; + + ClientCard enemyExtraMonster = Enemy.MonsterZone.Where(c => c != null && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(c)) + && (c.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) || (c.HasType(CardType.Link) && c.LinkCount >= 2)) + && CheckCanBeTargeted(c, canBeTarget, selfType) && CheckShouldNotIgnore(c)).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (enemyExtraMonster != null) return enemyExtraMonster; + + if (attack >= 0) + { + if (attack == 0) + attack = Util.GetBestAttack(Bot); + ClientCard betterCard = Enemy.MonsterZone.Where(card => card != null + && card.GetDefensePower() >= attack && card.GetDefensePower() > 0 && card.IsAttack() && CheckCanBeTargeted(card, canBeTarget, selfType) + && (ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).OrderByDescending(card => card.Attack).FirstOrDefault(); + if (betterCard != null) return betterCard; + } + return null; + } + + public bool CheckCanBeTargeted(ClientCard card, bool canBeTarget, CardType selfType) + { + if (card == null) return true; + if (canBeTarget) + { + if (card.IsShouldNotBeTarget()) return false; + if (((int)selfType & (int)CardType.Monster) > 0 && card.IsShouldNotBeMonsterTarget()) return false; + if (((int)selfType & (int)CardType.Spell) > 0 && card.IsShouldNotBeSpellTrapTarget()) return false; + if (((int)selfType & (int)CardType.Trap) > 0 && (card.IsShouldNotBeSpellTrapTarget() && !card.IsDisabled())) return false; + } + return true; + } + + public bool CheckShouldNotIgnore(ClientCard cards, bool ignore = false) + { + return !ignore || (!currentDestroyCardList.Contains(cards) && !currentNegateCardList.Contains(cards)); + } + + public List GetProblematicEnemyCardList(bool canBeTarget = false, bool ignoreSpells = false, CardType selfType = 0) + { + List resultList = new List(); + + List floodagateList = Enemy.MonsterZone.Where(c => c?.Data != null && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (floodagateList.Count > 0) resultList.AddRange(floodagateList); + + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsFloodgate() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).ToList(); + if (problemEnemySpellList.Count > 0) resultList.AddRange(ShuffleList(problemEnemySpellList)); + + List dangerList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterDangerous() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (dangerList.Count > 0 && (Duel.Player == 0 || (Duel.Phase > DuelPhase.Main1 && Duel.Phase < DuelPhase.Main2))) resultList.AddRange(dangerList); + + List invincibleList = Enemy.MonsterZone.Where(c => c?.Data != null && !resultList.Contains(c) && !currentDestroyCardList.Contains(c) + && c.IsMonsterInvincible() && c.IsFaceup() && CheckCanBeTargeted(c, canBeTarget, selfType)).OrderByDescending(card => card.Attack).ToList(); + if (invincibleList.Count > 0) resultList.AddRange(invincibleList); + + List enemyMonsters = Enemy.GetMonsters().Where(c => !currentDestroyCardList.Contains(c)).OrderByDescending(card => card.Attack).ToList(); + if (enemyMonsters.Count > 0) + { + foreach (ClientCard target in enemyMonsters) + { + if ((target.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz) + || (target.HasType(CardType.Link) && target.LinkCount >= 2)) + && !resultList.Contains(target) && CheckCanBeTargeted(target, canBeTarget, selfType)) + { + resultList.Add(target); + } + } + } + + List spells = Enemy.GetSpells().Where(c => c.IsFaceup() && !currentDestroyCardList.Contains(c) + && c.HasType(CardType.Equip | CardType.Pendulum | CardType.Field | CardType.Continuous) && CheckCanBeTargeted(c, canBeTarget, selfType) + && !notToDestroySpellTrap.Contains(c.Id)).ToList(); + if (spells.Count > 0 && !ignoreSpells) resultList.AddRange(ShuffleList(spells)); + + return resultList; + } + + public List GetNormalEnemyTargetList(bool canBeTarget = true, bool ignoreCurrentDestroy = false, CardType selfType = 0) + { + List targetList = GetProblematicEnemyCardList(canBeTarget, selfType: selfType); + List enemyMonster = Enemy.GetMonsters().Where(card => card.IsFaceup() && !targetList.Contains(card) + && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList(); + enemyMonster.Sort(CardContainer.CompareCardAttack); + enemyMonster.Reverse(); + targetList.AddRange(enemyMonster); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && enemyPlaceThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetSpells().Where(card => (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card)) && !enemyPlaceThisTurn.Contains(card)).ToList())); + targetList.AddRange(ShuffleList(Enemy.GetMonsters().Where(card => card.IsFacedown() && (!ignoreCurrentDestroy || !currentDestroyCardList.Contains(card))).ToList())); + + return targetList; + } + + public List GetMonsterListForTargetNegate(bool canBeTarget = false, CardType selfType = 0) + { + List resultList = new List(); + if (CheckWhetherNegated()) + { + return resultList; + } + + ClientCard target = Enemy.MonsterZone.FirstOrDefault(card => card?.Data != null + && card.IsMonsterShouldBeDisabledBeforeItUseEffect() && card.IsFaceup() && !card.IsShouldNotBeTarget() + && CheckCanBeTargeted(card, canBeTarget, selfType) + && !currentNegateCardList.Contains(card)); + if (target != null) + { + resultList.Add(target); + } + + foreach (ClientCard chainingCard in Duel.CurrentChain) + { + if (chainingCard.Location == CardLocation.MonsterZone && chainingCard.Controller == 1 && !chainingCard.IsDisabled() + && CheckCanBeTargeted(chainingCard, canBeTarget, selfType) && !currentNegateCardList.Contains(chainingCard)) + { + resultList.Add(chainingCard); + } + } + + return resultList; + } + + public ClientCard GetBestEnemyMonster(bool onlyFaceup = false, bool canBeTarget = false) + { + ClientCard card = GetProblematicEnemyMonster(0, canBeTarget); + if (card != null) return card; + card = Enemy.MonsterZone.GetHighestAttackMonster(canBeTarget); + if (card != null) return card; + List monsters = Enemy.GetMonsters(); + if (monsters.Count > 0 && !onlyFaceup) return ShuffleCardList(monsters)[0]; + return null; + } + + public ClientCard GetBestEnemySpell(bool onlyFaceup = false, bool canBeTarget = false) + { + List problemEnemySpellList = Enemy.SpellZone.Where(c => c?.Data != null + && c.IsFloodgate() && c.IsFaceup() && (!canBeTarget || !c.IsShouldNotBeTarget())).ToList(); + if (problemEnemySpellList.Count > 0) + { + return ShuffleCardList(problemEnemySpellList)[0]; + } + + List spells = Enemy.GetSpells().Where(card => !(card.IsFaceup() && card.IsCode(_CardId.EvenlyMatched))).ToList(); + + List faceUpList = spells.Where(ecard => ecard.IsFaceup() && (ecard.HasType(CardType.Continuous) || ecard.HasType(CardType.Field) || ecard.HasType(CardType.Pendulum))).ToList(); + if (faceUpList.Count > 0) + { + return ShuffleCardList(faceUpList)[0]; + } + + if (spells.Count > 0 && !onlyFaceup) + { + return ShuffleCardList(spells)[0]; + } + + return null; + } + + public ClientCard GetBestEnemyCard(bool onlyFaceup = false, bool canBeTarget = false, bool checkGrave = false) + { + ClientCard card = GetBestEnemyMonster(onlyFaceup, canBeTarget); + if (card != null) return card; + + card = GetBestEnemySpell(onlyFaceup, canBeTarget); + if (card != null) return card; + + if (checkGrave && Enemy.Graveyard.Count > 0) + { + List graveMonsterList = Enemy.Graveyard.GetMatchingCards(c => c.IsMonster()).ToList(); + if (graveMonsterList.Count > 0) + { + graveMonsterList.Sort(CardContainer.CompareCardAttack); + graveMonsterList.Reverse(); + return graveMonsterList[0]; + } + return ShuffleCardList(Enemy.Graveyard.ToList())[0]; + } + + return null; + } + #endregion + + public override void OnChainSolved(int chainIndex) + { + ClientCard currentCard = Duel.GetCurrentSolvingChainCard(); + ClientCard solving = Duel.GetCurrentSolvingChainCard(); + bool neg = Duel.IsCurrentSolvingChainNegated(); + Logger.DebugWriteLine($"[CHAIN] Solved idx={chainIndex} negated={neg} solving={CardStr(solving)}"); + if (currentCard != null && !Duel.IsCurrentSolvingChainNegated() && currentCard.Controller == 1) + { + if (currentCard.IsCode(_CardId.MaxxC)) enemyActivateMaxxC = true; + if (currentCard.IsCode(CardId.Fuwalos)) enemyActivateMaxxC = true; + if (currentCard.IsCode(_CardId.LockBird)) enemyActivateLockBird = true; + if (currentCard.IsCode(_CardId.InfiniteImpermanence)) + { + for (int i = 0; i < 5; ++i) + { + if (Enemy.SpellZone[i] == currentCard) + { + infiniteImpermanenceList.Add(4 - i); + break; + } + } + } + } + } + public override void OnChainEnd() + { + escapeTargetList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + enemyActivateInfiniteImpermanenceFromHand = false; + for (int idx = enemyPlaceThisTurn.Count - 1; idx >= 0; idx--) + { + ClientCard checkTarget = enemyPlaceThisTurn[idx]; + if (checkTarget == null || (checkTarget.Location != CardLocation.SpellZone && checkTarget.Location != CardLocation.MonsterZone)) + { + enemyPlaceThisTurn.RemoveAt(idx); + } + } + if (thronePending && _throneStage == ThroneStage.None) + { thronePending = false; } + ResetThroneFlow(); + Logger.DebugWriteLine("[CHAIN] OnChainEnd"); + base.OnChainEnd(); + } + private void ResetThroneFlow() + { + Logger.DebugWriteLine($"[THRONE] Reset flow (was stage={_throneStage}, pending={thronePending})"); + thronePending = false; + throneSearched = false; + throneDesiredPick = 0; + _throneStage = ThroneStage.None; + } + public override void OnNewTurn() + { + if (Duel.Turn <= 1) { dimensionShifterCount = 0; } + + enemyActivateMaxxC = false; + enemyActivateLockBird = false; + enemyActivateInfiniteImpermanenceFromHand = false; + if (dimensionShifterCount > 0) dimensionShifterCount--; + infiniteImpermanenceList.Clear(); + currentNegateCardList.Clear(); + currentDestroyCardList.Clear(); + sendToGYThisTurn.Clear(); + activatedCardIdList.Clear(); + enemyPlaceThisTurn.Clear(); + summonThisTurn.Clear(); + + // reset Throne flow + thronePending = false; + throneSearched = false; + throneDesiredPick = 0; + _gateReviveTargetId = 0; + _gateDiscardPreferredId = 0; + _gateWantsRecycle = false; + + base.OnNewTurn(); + } + + // ===== Material safety logic (inspired by windbot patterns) ===== + private static readonly HashSet NEVER_SAC = new HashSet + { + CardId.PHANTOM_OF_YUBEL, + CardId.DDD_WAVE_HIGH_KING_CAESAR, + CardId.VARUDASN_FINAL_BRINGER, + CardId.SP_LITTLE_KNIGHT, + CardId.GORGONOFZIL, + CardId.UNCHAINDEDABOMINATION + }; + private bool InThroneFlow => thronePending || _throneStage != ThroneStage.None; + private int PriorityIndex(int id) + { + int idx = Array.IndexOf(LinkFodderPriority, id); + return idx >= 0 ? idx : 999; + } + private bool IsProtectedMaterial(ClientCard c, bool allowUseYubelForLink = false) + { + if (c == null) return true; + + if (NEVER_SAC.Contains(c.Id)) return true; + + if (c.EquipCards != null && c.EquipCards.Count > 0) return true; + if (c.HasType(CardType.Link) && c.LinkCount >= 2) return true; + if (c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz)) return true; + + return false; + } + private ClientCard[] GetSafeMaterials(int need) + { + bool allowYubel = ShouldUseYubelForLink(); + + ClientCard[] list = Bot.GetMonsters() + .Where(m => !IsProtectedMaterial(m, allowYubel)) + .OrderBy(m => PriorityIndex(m.Id)) + .ThenBy(m => m.Attack) + .Take(need) + .ToArray(); + + return list; + } + private bool ShouldUseYubelForLink() + { + if (Duel.Player != 0) return false; // Perform only during your turn. (ทำเฉพาะเทิร์นเรา) + List mons = Bot.GetMonsters().Where(m => m != null).ToList(); + if (mons.Count == 0) return false; + + bool twoOrLess = mons.Count <= 2; + + // There is a Link to climb into (มีลิงก์ที่อัปเกรดบอร์ดได้) + bool canMakeBetter; + + if (HasInExtra(CardId.FIENDSMITHS_REQUIEM)) + { + canMakeBetter = + HasInExtra(CardId.MOON_OF_THE_CLOSED_HEAVEN) || + HasInExtra(CardId.UNCHAINED_SOUL_OF_RAGE) || + HasInExtra(CardId.UNCHAINED_LORD_OF_YAMA) || + + //HasInExtra(CardId.MUCKRAKER_UNDERWORLD) || + //HasInExtra(CardId.FIENDSMITHS_SEQUENCE) || + HasInExtra(CardId.SP_LITTLE_KNIGHT); + //HasInExtra(CardId.GORGONOFZIL) ; + } + else + { + canMakeBetter = + HasInExtra(CardId.UNCHAINED_SOUL_OF_RAGE) || + HasInExtra(CardId.UNCHAINED_LORD_OF_YAMA) || + //HasInExtra(CardId.MUCKRAKER_UNDERWORLD) || + //HasInExtra(CardId.FIENDSMITHS_SEQUENCE) || + HasInExtra(CardId.SP_LITTLE_KNIGHT); + //HasInExtra(CardId.GORGONOFZIL); + } + + // If only 1–2 "Yubel" cards remain on the field, and you can climb into a Link Monster that provides an interrupt or extends your plays ⇒ Link them + // ถ้าบนสนามเหลือแต่ชิ้น Yubel 1-2 ใบ และสามารถอัปเกรดเป็นลิงก์ที่ให้ interrupt/มูฟได้ ⇒ ใช้ไปลิงก์เถอะ + return twoOrLess && canMakeBetter; + } + private static readonly int[] YubelCostPriority = new[] + { + CardId.SAMSARA_D_LOTUS, + CardId.FABLED_LURRIE, + CardId.DARK_BECKONING_BEAST, + CardId.CHAOS_SUMMONING_BEAST, + CardId.GRUESOME_GRAVE_SQUIRMER, + CardId.SALAMANGREAT_ALMIRAJ, + //CardId.FIENDSMITHS_SEQUENCE + }; + private bool HasInExtra(int id) + { + return Bot.ExtraDeck.Any(c => c != null && c.Id == id); + } + // --- State for Nightmare Throne prompt flow --- + + + #region Work Space #1 + private bool DontSelfNG() { return Duel.LastChainPlayer != 0; } + private int LowestBit(int mask) => mask & -mask; + + // Retrieve the mask for the monster slot pointed to by our link. (Only Main Monster Zone 0..4) + // เอา mask ของช่องมอนสเตอร์ที่ลิงก์เราชี้ (เฉพาะ Main Monster Zone 0..4) + private int GetMyLinkedMMZMask() + { + int mask = 0; + foreach (var m in Bot.GetMonsters()) + { + if (m == null || !m.IsFaceup() || !m.HasType(CardType.Link)) continue; + mask |= m.GetLinkedZones(); + } + // เอาเฉพาะ 5 โซนหลัก (บิต 0..4) + mask &= 0x1F; + return mask; + } + private bool S1_ActivateTerraformingForThrone() + { + if (Type != ExecutorType.Activate) return false; + if (Card.Id != CardId.TERRAFORMING) return false; + if (Bot.HasInHandOrInSpellZone(CardId.NIGHTMARE_THRONE)) { return false; } + AI.SelectCard(CardId.NIGHTMARE_THRONE); + return true; + } + + private bool ActNightmareThroneSearch() + { + if (Type != ExecutorType.Activate) return false; + if (Card.Location == CardLocation.Hand && Bot.HasInSpellZone(CardId.NIGHTMARE_THRONE)) return false; + + int pick = 0; + if (!Bot.HasInHand(CardId.SAMSARA_D_LOTUS) && CheckRemainInDeck(CardId.SAMSARA_D_LOTUS) > 0) + pick = CardId.SAMSARA_D_LOTUS; + else if (Bot.HasInHand(CardId.SAMSARA_D_LOTUS) && !Bot.HasInHand(CardId.DARK_BECKONING_BEAST) && CheckRemainInDeck(CardId.DARK_BECKONING_BEAST) > 0) + pick = CardId.DARK_BECKONING_BEAST; + else if (Bot.HasInHand(CardId.SAMSARA_D_LOTUS) && Bot.HasInHand(CardId.DARK_BECKONING_BEAST) && !Bot.HasInHand(CardId.CHAOS_SUMMONING_BEAST) && CheckRemainInDeck(CardId.CHAOS_SUMMONING_BEAST) > 0) + pick = CardId.CHAOS_SUMMONING_BEAST; + + thronePending = true; + throneSearched = false; + throneDesiredPick = pick; + + Logger.DebugWriteLine($"[THRONE] Activate search; desiredPick={(pick == 0 ? "(auto)" : pick.ToString())}"); + DumpChain("ThroneActivate"); + + return true; + } + + private bool NSDarkBeckoningBeast() + { + if (Duel.Phase != DuelPhase.Main1) return false; + if (Bot.HasInMonstersZone(CardId.DARK_BECKONING_BEAST)) + { + if (Bot.HasInHand(CardId.SAMSARA_D_LOTUS)) + { + return false; + } + } + return true; + } + + private bool ActDarkBeckoningBeast() + { + if (Duel.Phase != DuelPhase.Main1) return false; + if ((CheckRemainInDeck(CardId.SPIRIT_GATES) > 0) && !Bot.HasInSpellZone(CardId.SPIRIT_GATES)) + { AI.SelectCard(CardId.SPIRIT_GATES); return true; } + else if (CheckRemainInDeck(CardId.CHAOS_SUMMONING_BEAST) > 0) + { AI.SelectCard(CardId.CHAOS_SUMMONING_BEAST); return true; } + else { return false; } + } + + private bool S4_ActivateSpiritGates() + { + // Use from hand to search DBB/CSB. (ใช้จาก "มือ" เพื่อค้น DBB/CSB) + if (Type != ExecutorType.Activate) return false; + if (Card.Location != CardLocation.Hand) return false; + if (Bot.HasInSpellZone(CardId.SPIRIT_GATES, true, true)) return false; + + // Rule: If DBB is on the field, search for CSB (กฎ: ถ้ามี DBB อยู่ "บนสนาม" ให้ค้น CSB) + int pick = 0; + bool dbbOnField = Bot.HasInMonstersZone(CardId.DARK_BECKONING_BEAST, false, false, true); + + if (dbbOnField && CheckRemainInDeck(CardId.CHAOS_SUMMONING_BEAST) > 0) + pick = CardId.CHAOS_SUMMONING_BEAST; + else if (CheckRemainInDeck(CardId.DARK_BECKONING_BEAST) > 0) + pick = CardId.DARK_BECKONING_BEAST; + else if (CheckRemainInDeck(CardId.CHAOS_SUMMONING_BEAST) > 0) + pick = CardId.CHAOS_SUMMONING_BEAST; + + if (pick == 0) return false; + + AI.SelectCard(pick); + return true; + } + private bool Gate_RecycleContinuous() + { + // e3: If Level 10 -> Retrieve a Continuous Spell from the Graveyard (prioritize NIGHTMARE_PAIN). + // e3: มีเลเวล 10 -> เก็บ Continuous Spell จากสุสาน (เน้นเก็บ NIGHTMARE_PAIN) + if (Card.Location != CardLocation.SpellZone) return false; + if (!HaveFaceupLevel10()) return false; + + if (!Bot.HasInGraveyard(CardId.NIGHTMARE_PAIN)) + return false; + + _gateWantsRecycle = true; + // Prioritize selecting "Nightmare Pain"; if it is unavailable, then choose "Paradise". + // เลือก Pain ก่อน ถ้าไม่มีค่อยเลือก Paradise ตอน select card + return true; + } + private bool Is00FiendId(int id) + { + // List of 0/0 Fiends used in this deck (รายชื่อ 0/0 Fiend ที่เราใช้ในเด็คนี้) + return id == CardId.YUBEL + || id == CardId.SPIRIT_OF_YUBEL + || id == CardId.DARK_BECKONING_BEAST + || id == CardId.CHAOS_SUMMONING_BEAST + || id == CardId.SAMSARA_D_LOTUS; + } + + // Select a target to revive, prioritizing according to 2.2.x. (เลือกเป้าหมายที่จะชุบ ด้วยลำดับความสำคัญตาม 2.2.x) + private int PlanSpiritGatesReviveTarget() + { + // Emergency Case + if (Bot.HasInMonstersZone(CardId.SALAMANGREAT_ALMIRAJ)&&Bot.HasInGraveyard(CardId.DARK_BECKONING_BEAST)) + { return CardId.DARK_BECKONING_BEAST; } + + // 2.2.2: If you lack a Spirit → Special Summon a Spirit (from the Graveyard or discarded from the hand). + // 2.2.2: ถ้าขาด spirit → SS spirit (จากสุสาน หรือทิ้งจากมือ) + bool spiritOnBoard = Bot.HasInMonstersZone(CardId.SPIRIT_OF_YUBEL, true); + bool spiritInGY = Bot.HasInGraveyard(CardId.SPIRIT_OF_YUBEL); + bool spiritInHand = Bot.HasInHand(CardId.SPIRIT_OF_YUBEL); + if (!spiritOnBoard && (spiritInGY || spiritInHand)) + return CardId.SPIRIT_OF_YUBEL; + + // 2.2.3: If DBB gets interrupted while Almiraj is on the field → Revive DBB to climb into Moon. + // 2.2.3: ถ้า DBB โดนขัด/ตันเกมแล้วมี Almiraj → ชุบ DBB เพื่อไต่ไป Moon + if (Bot.HasInMonstersZone(CardId.SALAMANGREAT_ALMIRAJ, true) + && Bot.HasInGraveyard(CardId.DARK_BECKONING_BEAST) + && HasInExtra(CardId.MOON_OF_THE_CLOSED_HEAVEN)) + return CardId.DARK_BECKONING_BEAST; + + // 2.2.1: If you lack lotus → SS lotus + if (!Bot.HasInMonstersZone(CardId.SAMSARA_D_LOTUS, true) && Bot.HasInGraveyard(CardId.SAMSARA_D_LOTUS)) + return CardId.SAMSARA_D_LOTUS; + + // General options (ทางเลือกทั่วไป): DBB > CSB > Yubel + if (Bot.HasInGraveyard(CardId.DARK_BECKONING_BEAST)) return CardId.DARK_BECKONING_BEAST; + if (Bot.HasInGraveyard(CardId.CHAOS_SUMMONING_BEAST)) return CardId.CHAOS_SUMMONING_BEAST; + if (Bot.HasInGraveyard(CardId.YUBEL)) return CardId.YUBEL; + + // ถ้าไม่มีอะไรชุบได้เลย อาจวางแผน discard ให้ Spirit ลงสุสานแล้วค่อยชุบ + if (!spiritOnBoard && spiritInHand) return CardId.SPIRIT_OF_YUBEL; + + return 0; + } + + // Select a card to discard: CSB > Paradise > Terror > (some cases) Spirit + private int PickSpiritGatesDiscard(int reviveTargetId) + { + // เคสอยากชุบ Spirit แต่ยังอยู่ในมือ → ทิ้ง Spirit เป็น cost แล้วชุบมันเอง + if (reviveTargetId == CardId.SPIRIT_OF_YUBEL && Bot.HasInHand(CardId.SPIRIT_OF_YUBEL)) + return CardId.SPIRIT_OF_YUBEL; + + if (Bot.HasInHand(CardId.FIENDSMITHS_PARADISE)) return CardId.FIENDSMITHS_PARADISE; + if (Bot.HasInHand(CardId.CHAOS_SUMMONING_BEAST)) return CardId.CHAOS_SUMMONING_BEAST; + if (Bot.HasInHand(CardId.YUBEL_TERROR_INCARNATE)) return CardId.YUBEL_TERROR_INCARNATE; + + // fallback: เลือกใบที่ "ทิ้งแล้วเจ็บน้อยสุด" + List hand = Bot.Hand.ToList(); + hand.Sort((a, b) => ScoreOwnCardForCost(a).CompareTo(ScoreOwnCardForCost(b))); + return hand.Count > 0 ? hand[0].Id : 0; + } + + private bool Gate_Revive00Fiend() + { + // e2: ทิ้งมือ 1 → SS Fiend 0/0 จากสุสาน + if (Card.Location != CardLocation.SpellZone) return false; + + if (requiemSummoned) + { + if (!Bot.HasInHandOrInGraveyard(CardId.SPIRIT_OF_YUBEL)) return false; // ต้องมีในหลุม + if (Bot.Hand.Count <= 0) return false; // ต้องมีใบทิ้ง + _gateReviveTargetId = CardId.SPIRIT_OF_YUBEL; + _gateDiscardPreferredId = PickSpiritGatesDiscard(_gateReviveTargetId); + _gateWantsRecycle = false; + return true; + } + + // วางแผนก่อนว่าจะชุบตัวไหน + int target = PlanSpiritGatesReviveTarget(); + if (target == 0) return false; + + // ต้องมีการ์ดให้ทิ้งอย่างน้อย 1 ใบ + if (Bot.Hand.Count <= 0) return false; + + int discard = PickSpiritGatesDiscard(target); + if (discard == 0) return false; + + _gateReviveTargetId = target; + _gateDiscardPreferredId = discard; + _gateWantsRecycle = false; + + // ไม่เลือกอะไรตรงนี้ ปล่อยให้ OnSelectCard จิ้ม cost/เป้าหมายให้ + return true; + } + + private bool HaveFaceupLevel10() + { + return Bot.MonsterZone.Any(m => m != null && m.IsFaceup() && m.Level == 10); + } + private bool ActNightmarePainHand() + { + if (Bot.HasInSpellZone(CardId.NIGHTMARE_PAIN, true, true)) return false; + if (Card.Location == CardLocation.Hand) return true; + return false; + } + + private bool ActNightmarePainEffect() + { + if (Card.Location == CardLocation.SpellZone) + { + if(CheckRemainInDeck(CardId.GRUESOME_GRAVE_SQUIRMER)==0)return false; + + if (Bot.HasInMonstersZone(CardId.SPIRIT_OF_YUBEL) || Bot.HasInHand(CardId.SPIRIT_OF_YUBEL)) + { + AI.SelectCard(CardId.SPIRIT_OF_YUBEL); + AI.SelectNextCard(CardId.GRUESOME_GRAVE_SQUIRMER); + return true; + } + } + return false; + } + + private bool S6_ChainThroneFollowUp() + { + if (Type != ExecutorType.Activate) return false; + if (sendToGYThisTurn.Any(c => c != null && YUBEL_SET.Contains(c.Id)) && Bot.HasInHand(CardId.NIGHTMARE_THRONE)) + { + AI.SelectYesNo(true); + return true; + } + return false; + } + + private bool NSSamsaraDLotus() + { + if (Bot.HasInMonstersZone(CardId.SPIRIT_OF_YUBEL)) return false; + return true; + } + + private bool ActSamsaraDLotus() + { + if (Duel.Player == 0) + { + if (Card.Location == CardLocation.MonsterZone) + { + AI.SelectCard(CardId.SPIRIT_OF_YUBEL); + return true; + } + } + if (Duel.Player == 1) + { + if (Bot.HasInMonstersZone(CardId.YUBEL) || Bot.HasInMonstersZone(CardId.SPIRIT_OF_YUBEL)) + { + return true; + } + } + return false; + } + private bool ActSamsaraDLotusGY() + { + if (Card.Location == CardLocation.Grave) + { + if (Bot.HasInMonstersZone(CardId.YUBEL)) + { + AI.SelectOption(1); + return true; + } + } + return false; + } + + private bool ActTract() + { + if (Card.Location != CardLocation.Hand) return false; + + // ถ้าเข้า Fiendsmith line แล้ว -> ไม่ใช้ Tract + if (requiemSummoned) return false; + AI.SelectCard(CardId.FABLED_LURRIE); + AI.SelectNextCard(CardId.FABLED_LURRIE); + return true; + /*if (Card.Location == CardLocation.Hand) + { + if (CheckRemainInDeck(CardId.FIENDSMITH_ENGRAVER) == 0 && + CheckRemainInDeck(CardId.LACRIMA_CT) == 0) + { + return false; + } + if(CheckRemainInDeck(CardId.FABLED_LURRIE) == 0){ return false; } + AI.SelectCard(CardId.FABLED_LURRIE); + AI.SelectNextCard(CardId.FABLED_LURRIE); + return true; + } + if (Card.LastLocation == CardLocation.Grave) { return false; } + return false;*/ + } + + private bool ActParadise() + { + if (Card.Location != CardLocation.Grave) return false; + if (Bot.HasInMonstersZoneOrInGraveyard(CardId.FIENDSMITHS_DESIRAE) || Bot.HasInBanished(CardId.FIENDSMITHS_DESIRAE)) return false; + AI.SelectCard(CardId.FIENDSMITHS_DESIRAE); + return DontSelfNG(); + } + + private bool ActDesirae() + { + if (Card.Location != CardLocation.Grave) {return false; } + ClientCard target = GetBestEnemyCard(onlyFaceup: true, canBeTarget: true, checkGrave: false); + if (target == null) return false; + if (Bot.HasInGraveyard(CardId.FIENDSMITHS_REQUIEM)) + { + AI.SelectCard(CardId.FIENDSMITHS_REQUIEM); + AI.SelectNextCard(target); + return true; + } + AI.SelectCard(target); + return true; + } + + private bool ActRequiemMZ() + { + if (Card.Location != CardLocation.MonsterZone) { return false; } + if (Bot.HasInHand(CardId.LACRIMA_CT) || CheckRemainInDeck(CardId.LACRIMA_CT) > 0) + { + AI.SelectCard(CardId.LACRIMA_CT); + AI.SelectPosition(CardPosition.FaceUpDefence); + return true; + } + else if (Bot.HasInHand(CardId.FIENDSMITH_ENGRAVER) || CheckRemainInDeck(CardId.FIENDSMITH_ENGRAVER) > 0) + { + AI.SelectCard(CardId.FIENDSMITH_ENGRAVER); + AI.SelectPosition(CardPosition.FaceUpDefence); + return true; + } + return false; + } + + private bool ActRequiemEQ() + { + if (!HasInExtra(CardId.NECROQUIP)) { return false; } + if (Card.Location != CardLocation.Grave) { return false; } + if (Bot.HasInMonstersZone(CardId.LACRIMA_CT)) + { + AI.SelectCard(CardId.LACRIMA_CT); + return true; + } + else if (Bot.HasInMonstersZone(CardId.FIENDSMITH_ENGRAVER)) + { + AI.SelectCard(CardId.FIENDSMITH_ENGRAVER); + return true; + } + return false; + } + + private bool SSNecroquip() + { + if (Bot.HasInSpellZone(CardId.FIENDSMITHS_REQUIEM) && Bot.HasInMonstersZone(CardId.LACRIMA_CT)) + { + AI.SelectCard(CardId.FIENDSMITHS_REQUIEM); + AI.SelectNextCard(CardId.LACRIMA_CT); + return true; + } + else if (Bot.HasInSpellZone(CardId.FIENDSMITHS_REQUIEM) && Bot.HasInMonstersZone(CardId.FIENDSMITH_ENGRAVER)) + { + AI.SelectCard(CardId.FIENDSMITHS_REQUIEM); + AI.SelectNextCard(CardId.FIENDSMITH_ENGRAVER); + return true; + } + return false; + } + + private bool ActLacimaCT() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(CardId.FIENDSMITH_ENGRAVER) && !Bot.HasInBanished(CardId.FIENDSMITH_ENGRAVER)) + { AI.SelectCard(CardId.FIENDSMITH_ENGRAVER); return true; } + else if (!Bot.HasInHandOrInMonstersZoneOrInGraveyard(CardId.FIENDSMITHS_PARADISE) && !Bot.HasInBanished(CardId.FIENDSMITHS_PARADISE)) + { AI.SelectCard(CardId.FIENDSMITHS_PARADISE); return true; } + return false; + } + return false; + } + + private bool ActLacimaCTGY() + { + if (Card.Location == CardLocation.Grave) + { + if (Bot.HasInBanished(CardId.FIENDSMITHS_PARADISE) || Bot.HasInHandOrInGraveyard(CardId.FIENDSMITHS_PARADISE)) + { return false; } + else { return DontSelfNG(); } + } + return false; + } + + /*private bool SSVarudras() + { + if (Bot.HasInMonstersZone(CardId.YUBEL) && Bot.HasInMonstersZone(CardId.SPIRIT_OF_YUBEL)) + { + AI.SelectCard(CardId.YUBEL); + AI.SelectNextCard(CardId.SPIRIT_OF_YUBEL); + return true; + } + return true; + }*/ + + private bool ActEngraverHand() + { + if (Card.Location != CardLocation.Hand) return false; + if (!Bot.HasInHandOrInSpellZoneOrInGraveyard(CardId.FIENDSMITH_TRACT) && !Bot.HasInBanished(CardId.FIENDSMITH_TRACT)) + { + AI.SelectCard(CardId.FIENDSMITH_TRACT); + return true; + } + return false; + } + + private bool ActEngraverGY() + { + if (Card.Location != CardLocation.Grave) return false; + if (Bot.HasInGraveyard(CardId.FABLED_LURRIE)) { AI.SelectCard(CardId.FABLED_LURRIE); return true; } + else if (Bot.HasInGraveyard(CardId.MOON_OF_THE_CLOSED_HEAVEN)) { AI.SelectCard(CardId.MOON_OF_THE_CLOSED_HEAVEN); return true; } + return false; + } + + private bool SSMoon() + { + if (moonSummoned){ return false; } + if (requiemSummoned) { return false; } + if (!HasInExtra(CardId.FIENDSMITHS_REQUIEM)) + return false; + ClientCard[] mats = GetSafeMaterials(2); + if (mats.Length < 2) return false; + AI.SelectMaterials(mats); + moonSummoned = true; + return true; + } + + private bool ActAbo() + { + if (Bot.HasInGraveyard(CardId.UNCHAINED_SOUL_OF_RAGE)) + { + AI.SelectCard(CardId.UNCHAINED_SOUL_OF_RAGE); + return true; + } + return false; + } + + private bool AlmirajSummon() + { + if (Bot.GetMonsterCount() > 1) return false; + ClientCard mat = Bot.GetMonsters().First(); + if (mat.IsCode(new[] { CardId.DARK_BECKONING_BEAST })) + { + AI.SelectMaterials(mat); + return true; + } + return false; + } + + private bool SSGGS() + { + if (!DontSelfNG()) { return false; } + if (BlockIfThrone("GGS")) return false; + if (Duel.Player == 1) { return false; } + if (InThroneFlow) { return false; } + if (Card.Location != CardLocation.Hand) { return false; } + if (Bot.HasInMonstersZone(CardId.SPIRIT_OF_YUBEL)) + { + AI.SelectYesNo(true); + AI.SelectCard(CardId.SPIRIT_OF_YUBEL); + return true; + } + AI.SelectYesNo(false); + return true; + } + + private bool ActGGSGY() + { + if (Card.Location != CardLocation.Grave) { return false; } + if (Bot.HasInGraveyard(CardId.SPIRIT_OF_YUBEL)) + { + AI.SelectCard(CardId.SPIRIT_OF_YUBEL); + return true; + } + else { return false; } + } + private bool ActLittleKnight() + { + if (ActivateDescription == -1 || ActivateDescription == Util.GetStringId(CardId.SP_LITTLE_KNIGHT, 0)) + { + // banish card + List problemCardList = GetProblematicEnemyCardList(true, selfType: CardType.Monster); + problemCardList.AddRange(GetDangerousCardinEnemyGrave(false)); + problemCardList.AddRange(GetNormalEnemyTargetList(true, true, CardType.Monster)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => card.HasType(CardType.Monster)).OrderByDescending(card => card.Attack)); + problemCardList.AddRange(Enemy.Graveyard.Where(card => !card.HasType(CardType.Monster))); + if (problemCardList.Count() > 0) + { + AI.SelectCard(problemCardList); + activatedCardIdList.Add(Card.Id); + return true; + } + } + else if (ActivateDescription == Util.GetStringId(CardId.SP_LITTLE_KNIGHT, 1)) + { + ClientCard selfMonster = null; + foreach (ClientCard target in Bot.GetMonsters()) + { + if (Duel.ChainTargets.Contains(target) && !escapeTargetList.Contains(target)) + { + selfMonster = target; + break; + } + } + if (selfMonster == null) + { + if (Duel.Player == 1) + { + selfMonster = Bot.GetMonsters().Where(card => card.IsAttack()).OrderBy(card => card.Attack).FirstOrDefault(); + if (!Util.IsOneEnemyBetterThanValue(selfMonster.Attack, true)) selfMonster = null; + } + } + if (selfMonster != null) + { + ClientCard nextMonster = null; + List selfTargetList = Bot.GetMonsters().Where(card => card != selfMonster).ToList(); + if (Enemy.GetMonsterCount() == 0 && selfTargetList.Count() > 0) + { + selfTargetList.Sort(CompareUsableAttack); + nextMonster = selfTargetList[0]; + escapeTargetList.Add(nextMonster); + } + if (Enemy.GetMonsterCount() > 0) + { + nextMonster = GetBestEnemyMonster(true, true); + currentDestroyCardList.Add(nextMonster); + } + if (nextMonster != null) + { + AI.SelectCard(selfMonster); + AI.SelectNextCard(nextMonster); + escapeTargetList.Add(selfMonster); + activatedCardIdList.Add(Card.Id + 1); + return true; + } + } + } + + return false; + } + public int CompareUsableAttack(ClientCard cardA, ClientCard cardB) + { + if (cardA == null && cardB == null) + return 0; + if (cardA == null) + return -1; + if (cardB == null) + return 1; + int powerA = (cardA.IsDefense() && summonThisTurn.Contains(cardA)) ? 0 : cardA.Attack; + int powerB = (cardB.IsDefense() && summonThisTurn.Contains(cardB)) ? 0 : cardB.Attack; + if (powerA < powerB) + return -1; + if (powerA == powerB) + return CardContainer.CompareCardLevel(cardA, cardB); + return 1; + } + private bool ActRageQuickLink() + { + if (Card.Location != CardLocation.MonsterZone) return false; + // Quick Link เฉพาะเทิร์นศัตรูช่วง Main เพื่อสร้าง 1 interrupt + if (Duel.Player != 1) return false; + if (Duel.Phase < DuelPhase.Main1 || Duel.Phase > DuelPhase.Main2) return false; + if (!HasValidRageLinkCandidate()) return false; + + ClientCard target = GetBestEnemyMonster(onlyFaceup: true, canBeTarget: true); + if (target == null) return false; + + // ส่วนใหญ่เอนจินจะถามเลือกการ์ดฝั่งตรงข้ามที่จะใช้เป็นวัสดุ/หรือไล่ + AI.SelectCard(target); + return DontSelfNG(); + } + private bool SSPhantom() + { + List gyMat2Codes = new List { CardId.YUBEL_TERROR_INCARNATE, CardId.YUBEL, CardId.DARK_BECKONING_BEAST, CardId.CHAOS_SUMMONING_BEAST, CardId.SPIRIT_OF_YUBEL }; + if (!Bot.HasInGraveyard(gyMat2Codes)) + { + return false; + } + // select mat 1 + if (Bot.HasInHandOrInMonstersZoneOrInGraveyard(CardId.YUBEL_TERROR_INCARNATE)) + { + AI.SelectCard(CardId.YUBEL_TERROR_INCARNATE); + } + else if (Bot.HasInMonstersZoneOrInGraveyard(CardId.YUBEL)) + { + AI.SelectCard(CardId.YUBEL); + } + else if (Bot.HasInGraveyard(CardId.SPIRIT_OF_YUBEL)) + { + AI.SelectCard(CardId.SPIRIT_OF_YUBEL); + } + else + { + return false; + } + // วัตถุดิบใบที่ 2 จากสุสานตามที่จัดไว้ + AI.SelectNextCard(gyMat2Codes); + return true; + } + + private bool ActSharvara() + { + if (BlockIfThrone("Sharvara")) return false; + if (Duel.Player == 1) { return false; } + if (InThroneFlow) return false; + if (Card.Location != CardLocation.Hand) return false; + if (Bot.HasInMonstersZone(CardId.SPIRIT_OF_YUBEL)) + { + AI.SelectCard(CardId.SPIRIT_OF_YUBEL); + return true; + } + else if (Bot.HasInMonstersZone(CardId.YUBEL)) + { + AI.SelectCard(CardId.YUBEL); + return true; + } + + return false; + } + private bool ActSharvaraGY() + { + if (Card.Location != CardLocation.Grave) return false; + return true; + } + + /*private bool L2NoBrain() + { + ClientCard[] mats = GetSafeMaterials(2); + if (mats.Length < 2) return false; + + AI.SelectMaterials(mats); + return true; + }*/ + private bool ActVarudras() + { + if (CheckWhetherNegated()) return false; + + // รายการเป้า (ศัตรูก่อน ถ้าไม่มีค่อย fallback) + List targetList = GetNormalEnemyTargetList(true, true); + int desc = (int)ActivateDescription; + long d1 = Util.GetStringId(CardId.VARUDASN_FINAL_BRINGER, 1); // ใช้ทั้ง e1 (Negate) และ e2 (Battle Start destroy) + long d2 = Util.GetStringId(CardId.VARUDASN_FINAL_BRINGER, 2); // e3 (Destroyed -> destroy 1) + + Logger.DebugWriteLine("[Varudras] desc: " + desc + ", timing = " + CurrentTiming); + + + ClientCard enemyPick = targetList.FirstOrDefault(c => c != null && c.Controller == 1); + + // e1: Quick effect Negate (ฝั่งคู่ต่อสู้กดเอฟเฟกต์) + if (desc == d1 && Duel.LastChainPlayer == 1 && Duel.CurrentChain.Count > 0) + { + if (!CheckLastChainShouldNegated()) return false; + activatedCardIdList.Add(Card.Id); // แท็กว่าเป็น e1 + return true; // เอนจินจะจัดการ detach เป็น cost ให้เอง + } + + // e2: Battle Start ทำลาย 1 / e3: ถูกทำลายแล้วทำลาย 1 + if (desc == d1 || desc == d2 || desc == -1) + { + if (targetList.Count == 0) return false; + + // พยายามให้เลือกฝั่งศัตรูก่อน + if (enemyPick != null) + targetList.Insert(0, enemyPick); + else + { + // ถ้าไม่มีการ์ดฝั่งศัตรูให้เลือกเลย -> แทรกฝั่งเราที่ "เสียหายน้อยสุด" ไว้หัวลิสต์ + ClientCard selfBest = + Bot.GetMonsters().Concat(Bot.GetSpells()) + .Where(c => c != null) + .OrderBy(ScoreOwnCardForCost) + .FirstOrDefault(); + if (selfBest != null) targetList.Insert(0, selfBest); + } + + // log / tag effect ย่อย + if (desc == d1 && Duel.CurrentChain.Count == 0) activatedCardIdList.Add(Card.Id + 1); // e2 + if (desc == d2) activatedCardIdList.Add(Card.Id + 2); // e3 + + AI.SelectCard(targetList); + return true; + } + + return false; + } + + private bool ShouldVarudrasDetachForPop(ClientCard target) + { + if (target == null) return false; + if (target.IsFloodgate() || target.IsMonsterDangerous() || target.IsMonsterInvincible()) return true; + // ใช้สกอร์เดิม ๆ ที่เรามีเพื่อประเมินความ "คุ้ม" ของการถอดวัตถุดิบแลกกับ 1 ทำลาย + return ScoreEnemyCardForRemoval(target) >= 3000; + } + private bool ActYamaGY() + { + if (Card.Location != CardLocation.Grave) return false; + if (Bot.HasInGraveyard(CardId.SPIRIT_OF_YUBEL)) return false; + AI.SelectCard(CardId.SPIRIT_OF_YUBEL); + AI.SelectYesNo(false); + return true; + } + private bool ActYamaMZ() + { + if (CheckRemainInDeck(CardId.SHARVARA) == 0 ) return false; + AI.SelectCard(CardId.SHARVARA); + return true; + } + private bool SSRequiem() + { + if (CheckRemainInDeck(CardId.LACRIMA_CT) == 0 && !Bot.HasInHand(CardId.LACRIMA_CT)) { return false; } + requiemSummoned = true; + return true; + } + private bool L4ABOSS() + { + if (!HasInExtra(CardId.UNCHAINDEDABOMINATION)) return false; + + List mons = Bot.GetMonsters(); + ClientCard yama = mons.FirstOrDefault(m => m != null && m.Id == CardId.UNCHAINED_LORD_OF_YAMA); + ClientCard rage = mons.FirstOrDefault(m => m != null && m.Id == CardId.UNCHAINED_SOUL_OF_RAGE); + ClientCard yubel = mons.FirstOrDefault(m => m != null && m.Id == CardId.YUBEL); + ClientCard terror = mons.FirstOrDefault(m => m != null && m.Id == CardId.YUBEL_TERROR_INCARNATE); + + if (yama != null && rage != null && IsInEMZ(yama)) + { + AI.SelectMaterials(new[] { yama, rage }); + return true; + } + + if (rage != null && yubel != null && terror != null && IsInEMZ(rage)) + { + AI.SelectMaterials(new[] { rage, yubel, terror }); + return true; + } + + // 3) greedy: เลือกลิงก์ที่มีอยู่ก่อน (prefer Rage > Yama > อื่น ๆ) แล้วเติมการ์ดจนแต้มรวมครบ 4 + ClientCard firstLink = + rage ?? + yama ?? + mons.Where(m => m.HasType(CardType.Link)).OrderByDescending(m => m.LinkCount).FirstOrDefault(); + + if (firstLink != null) + { + // ไม่ใช้ของที่ไม่ควรสังเวย (NEVER_SAC) แต่ยอมใช้ Yubel/Terror ได้ + List pool = mons + .Where(m => m != firstLink && !NEVER_SAC.Contains(m.Id)) + .OrderBy(ScoreOwnCardForCost) // เสียน้อยสุดมาก่อน + .ToList(); + + List pick = new List { firstLink }; + int need = 4 - LinkValue(firstLink); + + foreach (var m in pool) + { + pick.Add(m); + need -= LinkValue(m); + if (need <= 0) break; + } + + if (need <= 0) + { + AI.SelectMaterials(pick.ToArray()); + return true; + } + + } + return false; + } + private int LinkValue(ClientCard c) => (c != null && c.HasType(CardType.Link)) ? Math.Max(1, c.LinkCount) : 1; + + private bool IsInEMZ(ClientCard c) + { + ClientCard[] mz = Bot.MonsterZone; + return (mz.Length > 5 && mz[5] == c) || (mz.Length > 6 && mz[6] == c); + } + public bool UnchainedAbominationActivate() + { + if (CheckWhetherNegated()) return false; + List targetList = GetNormalEnemyTargetList(true, true, CardType.Monster); + if (targetList.Count() == 0) return false; + long logDesc = (long)ActivateDescription; + if (logDesc >= Util.GetStringId(CardId.UNCHAINDEDABOMINATION, 0)) + { + logDesc = Util.GetStringId(CardId.UNCHAINDEDABOMINATION, 0) - 10; + } + Logger.DebugWriteLine("[UnchainedAbomination]desc: " + logDesc + ", timing = " + CurrentTiming); + if (ActivateDescription == Util.GetStringId(CardId.UNCHAINDEDABOMINATION, 0)) activatedCardIdList.Add(Card.Id); + if (ActivateDescription == Util.GetStringId(CardId.UNCHAINDEDABOMINATION, 1) || ActivateDescription == -1) activatedCardIdList.Add(Card.Id + 1); + if (ActivateDescription == Util.GetStringId(CardId.UNCHAINDEDABOMINATION, 2)) activatedCardIdList.Add(Card.Id + 2); + AI.SelectCard(targetList); + + return true; + } + private ClientCard[] GetSafeMaterialsExcluding(HashSet excludeIds, int need) + { + return Bot.GetMonsters() + .Where(m => m != null + && (excludeIds == null || !excludeIds.Contains(m.Id)) + && !IsProtectedMaterial(m) // ไม่ยอมแลกของสำคัญ (Link≥2 / Extra ฯลฯ) + ) + .OrderBy(m => PriorityIndex(m.Id)) + .ThenBy(m => m.Attack) + .Take(need) + .ToArray(); + } + + private bool CanMakeRageWithoutYama() + { + ClientCard[] mats = GetSafeMaterialsExcluding(new HashSet { CardId.UNCHAINED_LORD_OF_YAMA }, 2); + return mats.Length >= 2; + } + private bool L2YamaSetup() + { + ClientCard[] mats = GetSafeMaterials(2); + if (mats.Length < 2) return false; + AI.SelectMaterials(mats); + return true; + } + private bool L2RageKeepYama() + { + // ต้องมี Yama อยู่ก่อน และต้องมีวัตถุดิบอื่น 2 ใบ (ไม่นับ Yama) + if (!Bot.HasInMonstersZone(CardId.UNCHAINED_LORD_OF_YAMA, true)) return false; + if (!CanMakeRageWithoutYama()) return false; + + ClientCard[] mats = GetSafeMaterialsExcluding(new HashSet { CardId.UNCHAINED_LORD_OF_YAMA }, 2); + if (mats.Length < 2) return false; // ยังไม่พอ → รอก่อน อย่าฝืนใช้ Yama + + AI.SelectMaterials(mats); + return true; + } + private bool HasFreeEMZ() + { + // ปกติ MonsterZone มี 7 ช่อง (0..4 = MMZ, 5..6 = EMZ) + ClientCard[] mz = Bot.MonsterZone; + bool slot5Free = mz.Length > 5 && mz[5] == null; + bool slot6Free = mz.Length > 6 && mz[6] == null; + return slot5Free || slot6Free; + } + + private bool HasValidRageLinkCandidate() + { + if (HasInExtra(CardId.SP_LITTLE_KNIGHT)) return true; + if (HasInExtra(CardId.GORGONOFZIL) && HasFreeEMZ()) return true; + return false; + } + + #endregion + + // ======================= On Select Somethings ==================== + #region Work Space #2 + private bool YesNoFor(long desc, long cardId, int idx) + { + ChainInfo info = Duel.GetCurrentSolvingChainInfo(); + ClientCard card = Duel.GetCurrentSolvingChainCard(); + // ต้องทั้ง: คำอธิบายตรง + การ์ดบน chain ตอนนี้ตรง + return desc == Util.GetStringId(cardId, idx) + && ((info != null && info.IsCode(cardId)) || (card != null && card.IsCode(cardId))); + } + public override bool OnSelectYesNo(long desc) + { + Logger.DebugWriteLine($"[DEBUG] OnSelectYesNo: desc={desc}"); + ChainInfo info = Duel.GetCurrentSolvingChainInfo(); + ClientCard solving = Duel.GetCurrentSolvingChainCard(); + DumpChain("OnSelectYesNo"); + Logger.DebugWriteLine($"[THRONE] OnSelectYesNo desc={desc} stage={_throneStage} solving={CardStr(solving)}"); + if (info != null && info.ActivatePlayer == 1) + { return false; } + // --- Nightmare Throne --- + // idx อาจต่างกันตามสคริปต์ แต่แนวคิดคือ anchor กับ desc+solving เสมอ + if (solving != null && solving.IsCode(CardId.NIGHTMARE_THRONE)) + { + // เปิด map ช่วย debug ให้เห็นว่า desc ตรง index ไหนจริง ๆ + DebugThroneDescMap(desc); + + // ยังไม่ได้เริ่มค้น -> ตอบ YES เพื่อเข้าโหมดค้น + if (_throneStage == ThroneStage.None && !throneSearched) + { + _throneStage = ThroneStage.Searching; + return true; // YES เริ่มค้น + } + + // ค้นเสร็จแล้ว และกำลังถาม "จะทำลายไหม?" -> default = NO + if (_throneStage == ThroneStage.AwaitDestroyPrompt || throneSearched) + { + _throneStage = ThroneStage.None; + return false; // ไม่ทำลาย + } + + // กันเคส engine ถามซ้ำก่อนโชว์เด็ค: ตอบ YES ไป (จะไปเข้า OnSelectCard) + if (_throneStage == ThroneStage.Searching && !throneSearched) + return true; + } + // --- Muckraker --- + /*if (YesNoFor(desc, CardId.MUCKRAKER_UNDERWORLD, 0)) + { + if (Duel.ChainTargets.Contains(Card)) return false; // ตัวเองกำลังโดนเล็งทำลาย -> ไม่ใช้ + bool protectAlly = Duel.ChainTargets.Any(t => t != null && t.Controller == 0 + && t.Location == CardLocation.MonsterZone && !YUBEL_SET.Contains(t.Id)); + return protectAlly; + }*/ + + // --- Varudras: ถามถอดวัตถุดิบอีก 1 เพื่อทำลาย --- + if (YesNoFor(desc, CardId.VARUDASN_FINAL_BRINGER, 1)) + { + ClientCard best = GetBestEnemyCard(); + return best != null && ShouldVarudrasDetachForPop(best); + } + if (solving != null + && solving.IsCode(CardId.VARUDASN_FINAL_BRINGER) + && Duel.CurrentChain.Count > 0) // แปลว่าอยู่ใน e1 ไม่ใช่ e2 + { + // มีเป้าศัตรูให้ทำลายไหม? + ClientCard t = GetNormalEnemyTargetList(true, true).FirstOrDefault(c => c.Controller == 1); + if (t == null) return false; // ไม่มีเป้า → ไม่ถอด + return ShouldVarudrasDetachForPop(t); // มีเป้า → ใช้เกณฑ์เดิมตัดสิน + } + + // aux.Stringid(78371393,2) -> คำถาม "จะสังเวยไหม?" + if (YesNoFor(desc, CardId.YUBEL, 2)) + { + // มี Lotus "บนสนามเรา" ไหม + bool haveLotusOnField = Bot.GetMonsters().Any(m => m != null && m.Id == CardId.SAMSARA_D_LOTUS); + + // ถ้ามี จะตอบ YES และตั้งธงว่ากำลังจะเลือกตัวสังเวยให้ Yubel + _yubelWantsTribute = haveLotusOnField; + return haveLotusOnField; // YES ถ้ามี Lotus, NO ถ้าไม่มี => Yubel ระเบิดตัวเอง + } + + + return base.OnSelectYesNo(desc); + } + + // Safety net for any selection the specific executors didn't pre-select + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + Logger.DebugWriteLine($"[DEBUG] OnSelectCard: hint={hint} (0x{hint:X}), min={min}, max={max}, cancelable={cancelable}, candidates={cards?.Count ?? 0}"); + + bool isReleasePrompt = + hint == (long)HintMsg.Release || + hint.ToString().ToLower().Contains("release"); // กันเหนียว + ClientCard solving = Duel.GetCurrentSolvingChainCard(); + if (cards != null && cards.Count > 0) + { + // === Throne === + if (_throneStage == ThroneStage.Searching && solving != null && solving.IsCode(CardId.NIGHTMARE_THRONE) && !throneSearched && cards != null && cards.Count > 0) + { + throneSearched = true; + _throneStage = ThroneStage.AwaitDestroyPrompt; + + ClientCard chosen = null; + if (throneDesiredPick != 0) + chosen = cards.FirstOrDefault(c => c != null && c.Id == throneDesiredPick); + + // fallback: ถ้าเลือกตามใจไม่ได้ ให้เลือกใบ Yubel ที่มีค่าที่สุด/หรือใบแรก + if (chosen == null) + chosen = cards.FirstOrDefault(c => c != null && YUBEL_SET.Contains(c.Id)) ?? cards[0]; + + Logger.DebugWriteLine($"[THRONE] Search pick => {CardStr(chosen)}"); + return new[] { chosen }; + } + // === SPIRIT GATES selections === + if (Card != null && Card.Id == CardId.SPIRIT_GATES && cards != null && cards.Count > 0) + { + // 2.1: เลือก Continuous Spell จากสุสาน (Recycle) + if (_gateWantsRecycle) + { + ClientCard pain = cards.FirstOrDefault(c => c != null && c.Id == CardId.NIGHTMARE_PAIN); + if (pain != null) return new[] { pain }; + + // อะไรต่อมิอะไรที่เป็น Continuous Spell ถ้ามี + ClientCard anyCont = cards.FirstOrDefault(c => c != null && c.IsSpell() && c.HasType(CardType.Continuous)); + if (anyCont != null) return new[] { anyCont }; + } + + // เลือกทิ้งมือเป็น cost (ลิสต์ทั้งหมดมาจากมือเรา) + bool selectingDiscard = cards.All(c => c != null && c.Controller == 0 && c.Location == CardLocation.Hand); + if (selectingDiscard && _gateDiscardPreferredId != 0) + { + ClientCard want = cards.FirstOrDefault(c => c.Id == _gateDiscardPreferredId); + if (want != null) return new[] { want }; + + // fallback: ใช้สกอร์เดิม + List sorted = cards.OrderBy(ScoreOwnCardForCost).ToList(); + return new[] { sorted[0] }; + } + + // เลือกเป้าหมายชุบจากสุสาน (Fiend 0/0) + bool selectingGYTarget = cards.Any(c => c != null && c.Location == CardLocation.Grave); + + if (selectingGYTarget) + { + if (requiemSummoned) + { + ClientCard sp = cards.FirstOrDefault(c => c != null && c.Id == CardId.SPIRIT_OF_YUBEL); + if (sp != null) return new[] { sp }; + } + if (_gateReviveTargetId != 0) + { + ClientCard t = cards.FirstOrDefault(c => c != null && c.Id == _gateReviveTargetId); + if (t != null) return new[] { t }; + } + if (moonSummoned) + { + int[] prio = { + CardId.SPIRIT_OF_YUBEL, + CardId.YUBEL + }; + foreach (var id in prio) + { + ClientCard pick = cards.FirstOrDefault(c => c != null && c.Id == id); + if (pick != null) return new[] { pick }; + } + } + else + { + int[] prio = { + CardId.SPIRIT_OF_YUBEL, + CardId.SAMSARA_D_LOTUS, + CardId.DARK_BECKONING_BEAST, + CardId.CHAOS_SUMMONING_BEAST, + CardId.YUBEL + }; + foreach (var id in prio) + { + ClientCard pick = cards.FirstOrDefault(c => c != null && c.Id == id); + if (pick != null) return new[] { pick }; + } + } + + ClientCard any00 = cards.FirstOrDefault(c => c != null && Is00FiendId(c.Id)); + if (any00 != null) return new[] { any00 }; + } + } + // === Throne: Select card (เลือกการ์ดที่ค้นเจอ) === + if (Card.Id == CardId.NIGHTMARE_THRONE && _throneStage == ThroneStage.Searching && thronePending && !throneSearched) + { + throneSearched = true; + _throneStage = ThroneStage.AwaitDestroyPrompt; + ClientCard chosen = null; + if (throneDesiredPick != 0) chosen = cards.FirstOrDefault(c => c.Id == throneDesiredPick); + if (chosen == null) chosen = cards[0]; + + Logger.DebugWriteLine($"[THRONE] Search pick => {CardStr(chosen)}"); + return new[] { chosen }; + } + // --- Yubel is asking us to choose the tribute target --- + if (_yubelWantsTribute && isReleasePrompt && cards != null && cards.Count > 0) + { + ClientCard lotus = cards.FirstOrDefault(c => c != null && c.Id == CardId.SAMSARA_D_LOTUS); + if (lotus != null) + { + _yubelWantsTribute = false; // Clear flags (เคลียร์ธง) + return new[] { lotus }; // เลือก Lotus สังเวยตามที่ต้องการ + } + // เผื่อกรณีเอนจินส่งลิสต์มาแต่ดันไม่มี Lotus (ไม่ควรเกิด เพราะเราตอบ YES เมื่อมี Lotus) + _yubelWantsTribute = false; + // ปล่อยให้ base ตัดสินใจ หรือจะ return null ก็ได้ตามฐานของคุณ + } + // --- Varudras: เลือกเป้าหมายทำลาย --- + if (Card.Id == CardId.VARUDASN_FINAL_BRINGER && hint == 502 && cards != null && cards.Count > 0) + { + // พยายามเลือกฝั่งศัตรูก่อน (คัดใบที่อันตราย/ป่วนที่สุด) + ClientCard enemyPick = cards + .Where(c => c != null && c.Controller == 1) + .OrderByDescending(c => ScoreEnemyCardForRemoval(c)) + .FirstOrDefault(); + if (enemyPick != null) return new[] { enemyPick }; + + return new[] { cards[0] }; // fallback + } + // --- Abomination: เลือกเป้าหมายทำลาย --- + if (Card.Id == CardId.UNCHAINDEDABOMINATION && hint == 502 && cards != null && cards.Count > 0) + { + // พยายามเลือกฝั่งศัตรูก่อน (คัดใบที่อันตราย/ป่วนที่สุด) + ClientCard enemyPick = cards + .Where(c => c != null && c.Controller == 1) + .OrderByDescending(c => ScoreEnemyCardForRemoval(c)) + .FirstOrDefault(); + if (enemyPick != null) return new[] { enemyPick }; + + return new[] { cards[0] }; // fallback + } + // --- Rage Quick Link --- + if (solving != null && solving.IsCode(CardId.UNCHAINED_SOUL_OF_RAGE) && cards.Any(c => c != null && c.Location == CardLocation.Extra)) + { + // 1) เลือก S:P Little Knight ก่อนเสมอ ถ้ามี + ClientCard pickSP = cards.FirstOrDefault(c => c != null && c.Id == CardId.SP_LITTLE_KNIGHT); + if (pickSP != null) return new List { pickSP }; + + // 2) เลือก Gorgon เฉพาะเมื่อ EMZ ว่างเท่านั้น + ClientCard pickGorgon = null; + pickGorgon = cards.FirstOrDefault(c => c != null && c.Id == CardId.GORGONOFZIL); + + if (pickGorgon != null) + { + if (HasFreeEMZ()) + return new List { pickGorgon }; + // ถ้า EMZ ไม่ว่าง ห้ามเลือก Gorgon -> ไปหาใบอื่นต่อ + } + + // 3) fallback: ถ้ามีลิงก์ตัวอื่นที่ generic ก็เลือกใบแรกไปก่อน + return new List { cards[0] }; + } + } + + + return base.OnSelectCard(cards, min, max, hint, cancelable); + } + + // --- Scoring helpers ------------------------------------------------- + private int ScoreOwnCardForCost(ClientCard c) + { + if (c == null) return int.MaxValue; + int score = 5000; // lower is more expendable + if (NEVER_SAC.Contains(c.Id)) return int.MaxValue; + if (YUBEL_SET.Contains(c.Id)) return 9000; + if ((c.HasType(CardType.Link) && c.LinkCount >= 2) || c.HasType(CardType.Fusion | CardType.Synchro | CardType.Xyz)) score += 2000; + if (c.EquipCards != null && c.EquipCards.Count > 0) score += 1000; + int idx = Array.IndexOf(YubelCostPriority, c.Id); + if (idx >= 0) score = 10 + idx; + score += Math.Max(0, c.Attack / 100); + return score; + } + + private int ScoreEnemyCardForRemoval(ClientCard c) + { + if (c == null) return -1; + int s = 0; + if (c.IsFloodgate()) s += 6000; + if (c.IsMonsterDangerous()) s += 4000; + if (c.IsMonsterInvincible()) s += 3500; + if (c.EquipCards != null && c.EquipCards.Count > 0) s += 800; + if (c.HasType(CardType.Fusion | CardType.Ritual | CardType.Synchro | CardType.Xyz)) s += 700; + if (c.HasType(CardType.Link) && c.LinkCount >= 2) s += 700; + s += Math.Max(0, c.Attack); + return s; + } + + #endregion + + #region DEBUG + + private string CardStr(ClientCard c) + { + if (c == null) return "null"; + string loc = c.Location.ToString(); + string face = c.IsFaceup() ? "FU" : "FD"; + return $"{c.Name}#{c.Id} [{loc}] P{c.Controller} {face}"; + } + private void DumpChain(string tag = "") + { + Logger.DebugWriteLine($"[CHAIN]{(tag == "" ? "" : $" ({tag})")} turn={Duel.Turn} player={Duel.Player} phase={Duel.Phase} chainCount={Duel.CurrentChain.Count}"); + for (int i = 0; i < Duel.CurrentChain.Count; i++) + { + ClientCard c = Duel.CurrentChain[i]; + Logger.DebugWriteLine($" [{i}] {CardStr(c)}"); + } + ClientCard solving = Duel.GetCurrentSolvingChainCard(); + if (solving != null) + { + Logger.DebugWriteLine($" -> Solving: {CardStr(solving)} ActivateDescription={ActivateDescription}"); + } + if (Duel.ChainTargets != null && Duel.ChainTargets.Count > 0) + { + IEnumerable tg = Duel.ChainTargets.Where(t => t != null).Select(CardStr); + Logger.DebugWriteLine($" targets: {string.Join(" | ", tg)}"); + } + } + + private void DebugThroneDescMap(long incomingDesc) + { + for (int i = 0; i < 5; i++) + { + long sid = Util.GetStringId(CardId.NIGHTMARE_THRONE, i); + Logger.DebugWriteLine($"[THRONE] desc map i={i} strId={sid} match={(sid == incomingDesc)}"); + } + } + private bool BlockIfThrone(string who) + { + if (InThroneFlow) + { + Logger.DebugWriteLine($"[THRONE] BLOCKED {who} during Throne flow"); + return true; + } + return false; + } + + #endregion + + // ======================= END OF FILE ==================== + } +} diff --git a/Game/AI/Decks/ZefraExecutor.cs b/Game/AI/Decks/ZefraExecutor.cs new file mode 100644 index 00000000..ca7b6491 --- /dev/null +++ b/Game/AI/Decks/ZefraExecutor.cs @@ -0,0 +1,2214 @@ +using YGOSharp.OCGWrapper; +using YGOSharp.OCGWrapper.Enums; +using System.Collections.Generic; +using System.Linq; +using System; +using WindBot; +using WindBot.Game; +using WindBot.Game.AI; +namespace WindBot.Game.AI.Decks +{ + [Deck("Zefra", "AI_Zefra")] + class ZefraExecutor : DefaultExecutor + { + public class CardId + { + //main code + public const int PSY_FrameDriver = 49036338; + public const int Zefraath = 29432356; + public const int TheMightyMasterofMagic = 3611830; + public const int AstrographSorcerer = 76794549; + public const int DestrudotheLostDragon_Frisson = 5560911; + public const int SupremeKingGateZero = 96227613; + public const int MythicalBeastJackalKing = 27354732; + public const int SecretoftheYangZing = 58990362; + public const int FlameBeastoftheNekroz = 20773176; + public const int StellarknightZefraxciton = 22617205; + public const int SupremeKingDragonDarkwurm = 69610326; + public const int Blackwing_ZephyrostheElite = 14785765; + public const int ShaddollZefracore = 95401059; + public const int Raidraptor_SingingLanius = 31314549; + public const int SatellarknightZefrathuban = 96223501; + public const int Raider_Wing = 52159691; + public const int Zefraxi_TreasureoftheYangZing = 21495657; + public const int RitualBeastTamerZeframpilica = 57777714; + public const int ServantofEndymion = 92559258; + public const int PSY_FramegearGamma = 38814750; + public const int MechaPhantomBeastO_Lion = 72291078; + public const int MaxxC = 23434538; + public const int Deskbot001 = 94693857; + public const int JetSynchron = 9742784; + public const int DDLamia = 19580308; + public const int DDSavantKepler = 11609969; + public const int LightoftheYangZing = 61488417; + public const int Rank_Up_MagicSoulShaveForce = 23581825; + public const int SpellPowerMastery = 38943357; + public const int DragonShrine = 41620959; + public const int Terraforming = 73628505; + public const int ZefraProvidence = 74580251; + public const int FoolishBurial = 81439173; + public const int CalledbytheGrave = 24224830; + public const int DarkContractwiththGate = 46372010; + public const int OracleofZefra = 32354768; + public const int ZefraWar = 96073342; + public const int ZefraDivineStrike = 35561352; + public const int NinePillarsofYangZing = 57831349; + public const int OneforOne = 2295440; + + //extra code + public const int BorreloadSavageDragon = 27548199; + public const int Odd_EyesMeteorburstDragon = 80696379; + public const int F_A_DawnDragster = 33158448; + public const int Denglong_FirstoftheYangZing = 65536818; + public const int HeraldoftheArcLight = 79606837; + public const int TruKingofAllCalamities = 88581108; + public const int Raidraptor_ArsenalFalcon = 96157835; + public const int Raidraptor_ForceStrix = 73347079; + public const int SaryujaSkullDread = 74997493; + public const int MechaPhantomBeastAuroradon = 44097050; + public const int HeavymetalfoesElectrumite = 24094258; + public const int CrystronHalqifibrax = 50588353; + public const int Raidraptor_WiseStrix = 36429703; + public const int Linkuriboh = 41999284; + public const int PSY_FramelordOmega = 74586817; + + public const int MechaPhantomBeastToken = 44097051; + } + private bool opt_0 = false; + private bool opt_1 = false; + private bool opt_2 = false; + //edo false + private const bool IS_YGOPRO = true; + private const int P_ACTIVATE_DESC = 1160; + //private const int P_SPSUMMON_DESC = 1163; + private int p_count = 0; + private int spell_activate_count = 0; + private bool summoned = false; + private bool link_summoned = false; + private bool p_summoned = false; + private bool p_summoning = false; + private bool activate_SupremeKingDragonDarkwurm_1 = false; + private bool activate_p_Zefraath = false; + private bool activate_OracleofZefra = false; + private bool activate_ZefraProvidence = false; + private bool activate_SupremeKingDragonDarkwurm_2 = false; + private bool activate_JetSynchron = false; + private bool activate_Blackwing_ZephyrostheElite = false; + private bool activate_DragonShrine = false; + private bool activate_SpellPowerMastery = false; + private bool activate_DestrudotheLostDragon_Frisson = false; + private bool activate_DarkContractwiththGate = false; + private bool activate_SecretoftheYangZing = false; + private bool activate_ShaddollZefracore = false; + private bool activate_DDLamia = false; + private bool xyz_mode = false; + private bool Blackwing_ZephyrostheElite_activate = false; + private bool HeavymetalfoesElectrumite_activate = false; + private bool should_destory = false; + private List Odd_EyesMeteorburstDragon_materials = new List(); + private bool duel_start = true; + private int activate_count = 0; + private int summon_count = 0; + private bool enemy_activate = false; + private enum CustomMessage + { + Happy, + Angry, + Surprise + } + private static class Toos + { + public delegate bool Delegate(ClientCard card); + private static bool DefaultFunc(ClientCard card) + { + return true; + } + public static bool LinqAny(IList cards, Delegate @delegate = null) + { + if (cards == null) return false; + @delegate = @delegate ?? DefaultFunc; + return cards.Any(card => card != null && @delegate(card)); + } + public static bool LinqAll(IList cards, Delegate @delegate = null, bool flag = true) + { + if (cards == null) return false; + IList rcards = new List(cards); + if (flag) rcards = cards.Where(card => card != null).ToList(); + @delegate = @delegate ?? DefaultFunc; + return rcards.All(card => card != null && @delegate(card)); + } + public static int LinqCount(IList cards, Delegate @delegate = null) + { + if (cards == null) return -1; + @delegate = @delegate ?? DefaultFunc; + return cards.Count(card => card != null && @delegate(card)); + } + public static List LinqWhere(IList cards, Delegate @delegate = null) + { + if (cards == null) return new List(); + @delegate = @delegate ?? DefaultFunc; + return cards.Where(card => card != null && @delegate(card)).ToList(); + } + } + private class Func + { + private IList _parameters = new List(); + private List no_p_spsummon_ids = new List() + { + CardId.Zefraath + }; + private List selectCardList = null; + private List selectCardIdList = null; + public List GetSelectCardList() + { + if (selectCardList == null) + { + selectCardList = new List(); + } + else + { + selectCardList.Clear(); + } + return selectCardList; + } + public List GetSelectCardIdList() + { + if (selectCardIdList == null) + { + selectCardIdList = new List(); + } + else + { + selectCardIdList.Clear(); + } + return selectCardIdList; + } + public bool IsLocation(ClientCard card) + { + return card.Location == (CardLocation)_parameters[0]; + } + public bool IsCode(ClientCard card) + { + return card.IsCode((int)_parameters[0]); + } + public static bool IsCode(ClientCard card, params int[] ids) + { + if (card == null) return false; + foreach (var id in ids) + { + if (card.IsCode(id)) return true; + } + return false; + } + public bool HasSetCode(ClientCard card) + { + return card.HasSetcode((int)_parameters[0]); + } + public static bool HasSetCode(ClientCard card, params int[] set_codes) + { + if (card == null) return false; + foreach (var set_code in set_codes) + { + if (card.HasSetcode(set_code)) return true; + } + return false; + } + public static bool IsFaceUp(ClientCard card) + { + return card.IsFaceup(); + } + public bool HasAttribute(ClientCard card) + { + return card.HasAttribute((CardAttribute)_parameters[0]); + } + public bool HasRace(ClientCard card) + { + return card.HasRace((CardRace)_parameters[0]); + } + public bool HasLevel(ClientCard card) + { + return card.Level == (int)_parameters[0]; + } + public bool HasType(ClientCard card) + { + return card.HasType((CardType)_parameters[0]); + } + public static bool IsOnfield(ClientCard card) + { + return (card.Location & CardLocation.MonsterZone) > 0 || (card.Location & CardLocation.SpellZone) > 0; + } + public static Toos.Delegate NegateFunc(Toos.Delegate @delegate) + { + return card => { return !@delegate(card); }; + } + private void SetParameters(IList parameters) + { + ClearParameters(); + for (int i = 0; i < parameters?.Count(); ++i) + { + _parameters.Add(parameters[i]); + } + } + private void ClearParameters() + { + _parameters.Clear(); + } + public bool CardsCheckAny(IList cards, Toos.Delegate @delegate = null, params object[] parameters) + { + SetParameters(parameters); + return Toos.LinqAny(cards, @delegate); + } + public bool CardsCheckALL(IList cards, Toos.Delegate @delegate = null, bool all = true, params object[] parameters) + { + SetParameters(parameters); + return Toos.LinqAll(cards, @delegate, all); + } + public int CardsCheckCount(IList cards, Toos.Delegate @delegate = null, params object[] parameters) + { + SetParameters(parameters); + return Toos.LinqCount(cards, @delegate); + } + public List CardsCheckWhere(IList cards, Toos.Delegate @delegate = null, params object[] parameters) + { + SetParameters(parameters); + return Toos.LinqWhere(cards, @delegate); + } + public static List MergeList(params List[] lists) + { + List result = new List(); + foreach (var list in lists) + { + if (list == null) continue; + result.AddRange(list); + } + return result; + } + public List CardsIdToClientCards(IList cardsId, IList cardsList, bool uniqueId = true) + { + if (cardsList?.Count() <= 0 || cardsId?.Count() <= 0) return new List(); + List result = new List(); + cardsId = cardsId.Distinct().ToList(); + foreach (var cardid in cardsId) + { + List cards = CardsCheckWhere(cardsList, IsCode, cardid); + if (cards.Count <= 0) continue; + if (uniqueId) result.Add(cards.First()); + else result.AddRange(cards); + } + return result; + } + public static List ClientCardsToCardsId(IList cardsList, bool uniqueId = false, bool alias = false) + { + if (cardsList?.Count <= 0) return new List(); + List res = new List(); + foreach (var card in cardsList) + { + if (card == null) continue; + if (card.Alias != 0 && alias && !(res.Contains(card.Alias) & uniqueId)) res.Add(card.Alias); + else if (card.Id != 0 && !(res.Contains(card.Id) & uniqueId)) res.Add(card.Id); + } + return res; + } + //AIUtil + public static IList CheckSelectCount(AIUtil util, IList _selected, IList cards, int min, int max) + { + return _selected?.Count() <= 0 ? null : util.CheckSelectCount(_selected, cards, min, max); + } + public static List GetZoneCards(ClientField player, CardLocation loc, bool feceup = false, bool disable = false) + { + if (!feceup) disable = false; + List result = new List(); + if ((loc & CardLocation.Hand) > 0) result.AddRange(Toos.LinqWhere(player.Hand)); + if ((loc & CardLocation.MonsterZone) > 0) result.AddRange(Toos.LinqWhere(player.MonsterZone, card => !(!card.IsFaceup() & feceup) && !(!card.IsDisabled() & disable))); + if ((loc & CardLocation.SpellZone) > 0) result.AddRange(Toos.LinqWhere(player.SpellZone, card => !(!card.IsFaceup() & feceup) && !(!card.IsDisabled() & disable))); + if ((loc & CardLocation.PendulumZone) > 0) result.AddRange(Toos.LinqWhere(new List() { player.SpellZone[0], player.SpellZone[4] }, card => !(!card.IsFaceup() & feceup) && !(!card.IsDisabled() & disable))); + if ((loc & CardLocation.Grave) > 0) result.AddRange(Toos.LinqWhere(player.Graveyard)); + if ((loc & CardLocation.Removed) > 0) result.AddRange(Toos.LinqWhere(player.Banished, card => !(!card.IsFaceup() & feceup))); + if ((loc & CardLocation.Extra) > 0) result.AddRange(Toos.LinqWhere(player.ExtraDeck, card => !(!card.IsFaceup() & feceup))); + result = result.Distinct().ToList(); + return result; + } + public bool HasInZone(ClientField player, CardLocation loc, int id, bool feceup = false, bool disable = false) + { + return CardsCheckAny(GetZoneCards(player, loc, feceup, disable), IsCode, id); + } + public static bool SpellActivate(ClientCard card) + { + return card.Location == CardLocation.Hand || (card.Location == CardLocation.SpellZone && card.IsFacedown()); + } + public static bool PendulumActivate(int desc, ClientCard card) + { + return desc == P_ACTIVATE_DESC && card.Location == CardLocation.Hand; + } + private static Toos.Delegate GetPSpSummonLimilt(ClientCard pcard) + { + int setcode = -1; + int setcode2 = -1; + switch (pcard.Id) + { + case CardId.SecretoftheYangZing: + case CardId.Zefraxi_TreasureoftheYangZing: { setcode = 0xc4; setcode2 = 0x9e; break; } + case CardId.FlameBeastoftheNekroz: { setcode = 0xc4; setcode2 = 0xb4; break; } + case CardId.StellarknightZefraxciton: + case CardId.SatellarknightZefrathuban: { setcode = 0xc4; setcode2 = 0x109c; break; } + case CardId.RitualBeastTamerZeframpilica: { setcode = 0xc4; setcode2 = 0x10b5; break; } + case CardId.ShaddollZefracore: { setcode = 0xc4; setcode2 = 0x9d; break; } + case CardId.DDSavantKepler: { setcode = 0xaf; break; } + default: break; + } + return card => { + return (setcode == -1 ? true : card.HasSetcode(setcode)) + || (setcode2 == -1 ? true : card.HasSetcode(setcode2)); + }; + } + public static int[] GetPScales(ClientField bot) + { + int[] pScales = new int[2]; + ClientCard lcard = bot.SpellZone[0]; + ClientCard rcard = bot.SpellZone[4]; + pScales[0] = (lcard == null || lcard.IsFacedown() || !lcard.HasType(CardType.Pendulum)) ? -1 : lcard.RScale; + pScales[1] = (rcard == null || rcard.IsFacedown() || !rcard.HasType(CardType.Pendulum)) ? -1 : rcard.LScale; + return pScales; + } + public static int GetPScale(ClientField bot, int id) + { + bool rscale = false; + ClientCard pcard = null; + if (bot.SpellZone[0] != null && bot.SpellZone[0].Id == id) + { + pcard = bot.SpellZone[4]; + } + else + { + pcard = bot.SpellZone[0]; + rscale = true; + } + if (pcard == null || pcard.IsFacedown() || !pcard.HasType(CardType.Pendulum)) return -1; + return rscale ? pcard.RScale : pcard.LScale; + } + public List GetPSpSummonMonster(ClientField bot, ClientCard lcard, ClientCard rcard) + { + if (lcard == null || rcard == null || !lcard.HasType(CardType.Pendulum) || !rcard.HasType(CardType.Pendulum) || (IsOnfield(lcard) & lcard.IsFacedown()) || (IsOnfield(lcard) & rcard.IsFacedown())) return null; + int MaxScale = Math.Max(lcard.RScale, rcard.LScale); + int MinScale = Math.Min(lcard.RScale, rcard.LScale); + Toos.Delegate @llimit = GetPSpSummonLimilt(lcard); + Toos.Delegate @rlimit = GetPSpSummonLimilt(rcard); + return CardsCheckWhere(GetZoneCards(bot, CardLocation.Hand | CardLocation.Extra, true), + card => { + return card != lcard && card != rcard && card.HasType(CardType.Monster) && card.Level > MinScale && card.Level < MaxScale + && !no_p_spsummon_ids.Contains(card.Id) && @llimit(card) && @rlimit(card); + }); ; + } + public bool IsActivateScale(ClientField bot, ClientCard card) + { + ClientCard lcard = bot.SpellZone[0]; + ClientCard rcard = bot.SpellZone[4]; + List spSummonMonster = null; + if (lcard != null && rcard != null) return false; + if (lcard == null && rcard == null) return true; + + spSummonMonster = lcard == null ? GetPSpSummonMonster(bot, card, rcard) : GetPSpSummonMonster(bot, lcard, card); + return spSummonMonster?.Count() > 0; + } + public static int CompareCardScale(ClientCard cardA, ClientCard cardB) + { + if (cardA.RScale < cardB.RScale) + return -1; + if (cardA.RScale == cardB.RScale) + return 0; + return 1; + } + public static List GetCardsRepeatCardsId(IList cards) + { + if (cards?.Count <= 0) return new List() { -1 }; + IList cardsid = new List(); + List res = new List(); + foreach (var card in cards) + { + if (card == null) continue; + cardsid.Add(card.Id); + } + for (int i = 0; i < cardsid.Count; i++) + { + if (res.Count >= 0 && res.Contains(cardsid[i])) continue; + int times = 0; + for (int j = 0; j < cardsid.Count; j++) + { + if (times > 1) { res.Add(cardsid[i]); break; } + if (cardsid[i] == cardsid[j]) ++times; + } + } + if (res.Count <= 0) return new List() { -1 }; + return res; + } + } + private Func func = new Func(); + public ZefraExecutor(GameAI ai, Duel duel) : base(ai, duel) + { + AddExecutor(ExecutorType.Activate, CardId.CalledbytheGrave, CalledbytheGraveEffect); + AddExecutor(ExecutorType.Activate, CardId.BorreloadSavageDragon, BorreloadSavageDragonEffect_2); + AddExecutor(ExecutorType.Activate, CardId.F_A_DawnDragster, ResetFlag); + AddExecutor(ExecutorType.Activate, CardId.NinePillarsofYangZing, NinePillarsofYangZingEffect); + AddExecutor(ExecutorType.Activate, CardId.ZefraDivineStrike, ZefraDivineStrikeEffect); + AddExecutor(ExecutorType.Activate, CardId.HeraldoftheArcLight, HeraldoftheArcLightEffect); + AddExecutor(ExecutorType.Activate, CardId.TruKingofAllCalamities, TruKingofAllCalamitiesEffect); + AddExecutor(ExecutorType.Activate, CardId.PSY_FramegearGamma, ResetFlag); + AddExecutor(ExecutorType.Activate, CardId.MaxxC, DefaultMaxxC); + AddExecutor(ExecutorType.Activate, CardId.SupremeKingDragonDarkwurm, SupremeKingDragonDarkwurmEffect); + AddExecutor(ExecutorType.Activate, CardId.ServantofEndymion, ServantofEndymionEffect); + AddExecutor(ExecutorType.Activate, CardId.Terraforming, TerraformingEffect); + AddExecutor(ExecutorType.Activate, CardId.SpellPowerMastery, ResetFlag); + AddExecutor(ExecutorType.Activate, CardId.DragonShrine, DragonShrineEffect); + AddExecutor(ExecutorType.Activate, CardId.FoolishBurial, FoolishBurialEffect); + AddExecutor(ExecutorType.Activate, CardId.DarkContractwiththGate, DarkContractwiththGateEffect); + AddExecutor(ExecutorType.Activate, CardId.Terraforming, ResetFlag); + AddExecutor(ExecutorType.Activate, CardId.OracleofZefra, OracleofZefraEffect); + AddExecutor(ExecutorType.Activate, CardId.ZefraProvidence, ZefraProvidenceEffect); + AddExecutor(ExecutorType.Activate, CardId.AstrographSorcerer, AstrographSorcererEffect); + AddExecutor(ExecutorType.Activate, CardId.HeavymetalfoesElectrumite, HeavymetalfoesElectrumiteEffect); + AddExecutor(ExecutorType.Summon, CardId.SupremeKingDragonDarkwurm, SupremeKingDragonDarkwurmSummon); + AddExecutor(ExecutorType.Activate, CardId.SupremeKingGateZero, SupremeKingGateZeroEffect); + AddExecutor(ExecutorType.Activate, CardId.Zefraxi_TreasureoftheYangZing, Zefraxi_TreasureoftheYangZingEffect); + AddExecutor(ExecutorType.Activate, CardId.SatellarknightZefrathuban, SatellarknightZefrathubanEffect); + AddExecutor(ExecutorType.Activate, CardId.RitualBeastTamerZeframpilica, RitualBeastTamerZeframpilicaEffect); + AddExecutor(ExecutorType.Activate, CardId.SecretoftheYangZing, SecretoftheYangZingEffect); + AddExecutor(ExecutorType.Activate, CardId.FlameBeastoftheNekroz, FlameBeastoftheNekrozEffect); + AddExecutor(ExecutorType.Activate, CardId.ShaddollZefracore, ShaddollZefracoreEffect); + AddExecutor(ExecutorType.Activate, CardId.StellarknightZefraxciton, StellarknightZefraxcitonEffect); + AddExecutor(ExecutorType.Activate, CardId.SupremeKingDragonDarkwurm, SupremeKingGateZeroEffect); + AddExecutor(ExecutorType.Activate, CardId.AstrographSorcerer, SupremeKingGateZeroEffect); + AddExecutor(ExecutorType.Activate, CardId.Zefraath, ZefraathEffect); + AddExecutor(ExecutorType.Activate, CardId.DDSavantKepler, DDSavantKeplerEffect); + AddExecutor(ExecutorType.Summon, CardId.DDSavantKepler, DDSavantKeplerSummon); + AddExecutor(ExecutorType.Activate, CardId.ServantofEndymion, ServantofEndymionEffect_3); + AddExecutor(ExecutorType.Activate, CardId.MythicalBeastJackalKing, MythicalBeastJackalKingEffect); + AddExecutor(ExecutorType.SpSummon, Psummon); + AddExecutor(ExecutorType.Activate, CardId.OneforOne, OneforOneEffect); + AddExecutor(ExecutorType.Activate, CardId.ServantofEndymion, ServantofEndymionEffect_2); + AddExecutor(ExecutorType.Activate, CardId.CrystronHalqifibrax, CrystronHalqifibraxEffect); + AddExecutor(ExecutorType.SpSummon, CardId.Raidraptor_ArsenalFalcon, Raidraptor_ArsenalFalconSummon); + AddExecutor(ExecutorType.Activate, CardId.Raidraptor_ArsenalFalcon, Raidraptor_ArsenalFalconEffect); + AddExecutor(ExecutorType.SpSummon, CardId.HeavymetalfoesElectrumite, HeavymetalfoesElectrumiteSummon); + //xyz mode + AddExecutor(ExecutorType.SpSummon, CardId.Odd_EyesMeteorburstDragon, Odd_EyesMeteorburstDragonSummon); + AddExecutor(ExecutorType.Activate, CardId.Odd_EyesMeteorburstDragon, Odd_EyesMeteorburstDragonEffect); + AddExecutor(ExecutorType.SpSummon, CardId.Raidraptor_WiseStrix, Raidraptor_WiseStrixSummon); + AddExecutor(ExecutorType.Activate, CardId.Raidraptor_WiseStrix, Raidraptor_WiseStrixEffect); + AddExecutor(ExecutorType.Activate, CardId.Blackwing_ZephyrostheElite, Blackwing_ZephyrostheEliteEffect); + AddExecutor(ExecutorType.SpSummon, CardId.Raidraptor_ForceStrix, Raidraptor_ForceStrixSummon); + AddExecutor(ExecutorType.Activate, CardId.Raidraptor_ForceStrix, Raidraptor_ForceStrixEffect); + AddExecutor(ExecutorType.Activate, CardId.Rank_Up_MagicSoulShaveForce, ResetFlag); + AddExecutor(ExecutorType.Activate, CardId.Raider_Wing, Raider_WingEffect); + AddExecutor(ExecutorType.SpSummon, CardId.Raidraptor_SingingLanius); + //xyz mode + AddExecutor(ExecutorType.SpSummon, CardId.SaryujaSkullDread, SaryujaSkullDreadSummon); + AddExecutor(ExecutorType.Activate, CardId.SaryujaSkullDread, SaryujaSkullDreadEffect); + AddExecutor(ExecutorType.SpSummon, CardId.Denglong_FirstoftheYangZing, Denglong_FirstoftheYangZingSummon_2); + AddExecutor(ExecutorType.SpSummon, CardId.BorreloadSavageDragon, BorreloadSavageDragonSummon_2); + AddExecutor(ExecutorType.SpSummon, CardId.CrystronHalqifibrax, CrystronHalqifibraxSummon); + AddExecutor(ExecutorType.SpSummon, CardId.Linkuriboh, LinkuribohSummon); + AddExecutor(ExecutorType.Activate, CardId.DDLamia, DDLamiaEffect); + AddExecutor(ExecutorType.SpSummon, CardId.MechaPhantomBeastAuroradon, MechaPhantomBeastAuroradonSummon); + AddExecutor(ExecutorType.Activate, CardId.MechaPhantomBeastAuroradon, MechaPhantomBeastAuroradonEffect); + AddExecutor(ExecutorType.SpSummon, CardId.Denglong_FirstoftheYangZing, Denglong_FirstoftheYangZingSummon); + AddExecutor(ExecutorType.Activate, CardId.Denglong_FirstoftheYangZing, Denglong_FirstoftheYangZingEffect); + AddExecutor(ExecutorType.SpSummon, CardId.BorreloadSavageDragon, BorreloadSavageDragonSummon); + AddExecutor(ExecutorType.Activate, CardId.BorreloadSavageDragon, BorreloadSavageDragonEffect); + AddExecutor(ExecutorType.SpSummon, CardId.HeraldoftheArcLight); + AddExecutor(ExecutorType.SpSummon, CardId.F_A_DawnDragster, F_A_DawnDragsterSummon); + AddExecutor(ExecutorType.SpSummon, CardId.PSY_FramelordOmega, BorreloadSavageDragonSummon); + AddExecutor(ExecutorType.Activate, CardId.PSY_FramelordOmega, PSY_FramelordOmegaEffect); + AddExecutor(ExecutorType.Activate, CardId.Linkuriboh, LinkuribohEffect); + AddExecutor(ExecutorType.Activate, CardId.MechaPhantomBeastO_Lion); + AddExecutor(ExecutorType.Activate, CardId.JetSynchron, JetSynchronEffect); + AddExecutor(ExecutorType.Activate, CardId.Blackwing_ZephyrostheElite, Blackwing_ZephyrostheEliteEffect_2); + AddExecutor(ExecutorType.Summon, CardId.JetSynchron, DDLamiaSummon); + AddExecutor(ExecutorType.Summon, CardId.DDLamia, DDLamiaSummon); + AddExecutor(ExecutorType.Summon, CardId.Deskbot001, DDLamiaSummon); + AddExecutor(ExecutorType.Summon, CardId.LightoftheYangZing, DDLamiaSummon); + List p_summon_ids = new List() {CardId.Zefraxi_TreasureoftheYangZing,CardId.SatellarknightZefrathuban,CardId.ServantofEndymion,CardId.RitualBeastTamerZeframpilica, + CardId.DDSavantKepler,CardId.StellarknightZefraxciton,CardId.ShaddollZefracore,CardId.SupremeKingDragonDarkwurm}; + for (int i = 0; i < p_summon_ids.Count; ++i) AddExecutor(ExecutorType.Summon, p_summon_ids[i], DefaultSummon); + AddExecutor(ExecutorType.Summon, DefaultSummon); + AddExecutor(ExecutorType.Activate, CardId.Deskbot001, ResetFlag); + AddExecutor(ExecutorType.Activate, CardId.TheMightyMasterofMagic, TheMightyMasterofMagicEffect); + AddExecutor(ExecutorType.Activate, CardId.DestrudotheLostDragon_Frisson, DestrudotheLostDragon_FrissonEffect); + AddExecutor(ExecutorType.Summon, CardId.Blackwing_ZephyrostheElite, DefaultSummon_2); + AddExecutor(ExecutorType.Summon, DefaultSummon_2); + AddExecutor(ExecutorType.SpSummon, CardId.Linkuriboh); + AddExecutor(ExecutorType.SpellSet, SpellSet); + AddExecutor(ExecutorType.SpellSet, SpellSet_2); + AddExecutor(ExecutorType.Repos, DefaultMonsterRepos); + AddExecutor(ExecutorType.Activate, DefaultPActivate); + AddExecutor(ExecutorType.GoToEndPhase, GoToEndPhase); + } + public override void OnNewTurn() + { + if (duel_start) + { + duel_start = false; + AI.SendCustomChat((int)CustomMessage.Happy); + } + activate_SupremeKingDragonDarkwurm_1 = false; + activate_SupremeKingDragonDarkwurm_2 = false; + activate_JetSynchron = false; + activate_DestrudotheLostDragon_Frisson = false; + activate_ZefraProvidence = false; + activate_OracleofZefra = false; + activate_DragonShrine = false; + activate_p_Zefraath = false; + p_summoned = false; + summoned = false; + activate_DarkContractwiththGate = false; + activate_SecretoftheYangZing = false; + activate_ShaddollZefracore = false; + activate_SpellPowerMastery = false; + link_summoned = false; + activate_DDLamia = false; + xyz_mode = false; + Blackwing_ZephyrostheElite_activate = false; + HeavymetalfoesElectrumite_activate = false; + spell_activate_count = 0; + p_count = 0; + activate_count = 0; + summon_count = 0; + enemy_activate = false; + base.OnNewTurn(); + } + private bool ZefraProvidenceEffect() + { + if (ActivateDescription == 96) + { + if (should_destory) + { + should_destory = false; + return false; + } + return BeforeResult(ExecutorType.Activate); + } + else + { + activate_ZefraProvidence = true; + return BeforeResult(ExecutorType.Activate); + } + + } + private List CheckShouldSpsummonExtraMonster() + { + List extra_ids = new List() { CardId.HeavymetalfoesElectrumite, CardId.CrystronHalqifibrax }; + if (!Bot.HasInExtra(CardId.HeavymetalfoesElectrumite)) extra_ids.Remove(CardId.HeavymetalfoesElectrumite); + if (!Bot.HasInExtra(CardId.CrystronHalqifibrax)) extra_ids.Remove(CardId.CrystronHalqifibrax); + if (extra_ids.Count <= 0) return extra_ids; + bool DD_summon_check = false; + if (Bot.HasInExtra(CardId.CrystronHalqifibrax) && ((!summoned && HasInDeck(CardId.DDSavantKepler) && (HasInDeck(CardId.DarkContractwiththGate) || Bot.HasInHandOrInSpellZone(CardId.DarkContractwiththGate) + ) && !activate_DarkContractwiththGate && HasInDeck(CardId.DDLamia)) || (func.CardsCheckAny(Bot.Hand, func.HasType, CardType.Tuner) && + (HasInDeck(CardId.AstrographSorcerer) || Bot.HasInHand(CardId.AstrographSorcerer))))) + { + DD_summon_check = true; + } + if (Bot.SpellZone[0] != null && Bot.SpellZone[4] != null) + { + List spSummonMonster = func.GetPSpSummonMonster(Bot, Bot.SpellZone[0], Bot.SpellZone[4]); + if (DD_summon_check && spSummonMonster != null) + { + + List pSpsummonMonster = func.CardsCheckWhere(spSummonMonster, func.HasType, CardType.Pendulum); + List monsterCards = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.MonsterZone), card => + { return card.IsFaceup() && card.HasType(CardType.Pendulum); }); + if (Func.MergeList(pSpsummonMonster, monsterCards).Count <= 0) extra_ids.Remove(CardId.HeavymetalfoesElectrumite); + } + else + { + extra_ids.Remove(CardId.HeavymetalfoesElectrumite); + } + } + else + { + if (!((Bot.HasInHand(CardId.OracleofZefra) && !activate_OracleofZefra) || (Bot.HasInHand(CardId.ZefraProvidence) + && !activate_ZefraProvidence) || (Bot.HasInHand(CardId.Zefraath) && !activate_p_Zefraath))) + { + extra_ids.Clear(); + } + } + if (!DD_summon_check) extra_ids.Remove(CardId.HeavymetalfoesElectrumite); + return extra_ids; + } + private bool DDLamiaSummon() + { + if (!IsCanSynchroSummon(Card.Level)) return false; + if (Bot.HasInExtra(CardId.Linkuriboh) || (Bot.HasInExtra(CardId.CrystronHalqifibrax) && + Func.GetZoneCards(Bot, CardLocation.MonsterZone, true).Count > 0)) + { + summoned = true; + return BeforeResult(ExecutorType.Summon); + } + return false; + } + private bool XyzModeCheck(bool flag1 = false) + { + return !link_summoned && !(!Bot.HasInExtra(CardId.Raidraptor_ArsenalFalcon) & flag1) && HasInDeck(CardId.Blackwing_ZephyrostheElite) && Bot.HasInExtra(CardId.Raidraptor_ForceStrix) && Bot.HasInExtra(CardId.Raidraptor_WiseStrix) + && Bot.HasInExtra(CardId.TruKingofAllCalamities) && (HasInDeck(CardId.Raider_Wing) || Bot.HasInHand(CardId.Raider_Wing)) + && (HasInDeck(CardId.Raidraptor_SingingLanius) || Bot.HasInHand(CardId.Raidraptor_SingingLanius)) + && (HasInDeck(CardId.Rank_Up_MagicSoulShaveForce) || Bot.HasInHand(CardId.Rank_Up_MagicSoulShaveForce)); + } + private bool Raidraptor_ForceStrixEffect() + { + AI.SelectCard(CardId.Raider_Wing); + AI.SelectNextCard(CardId.Raidraptor_SingingLanius); + return BeforeResult(ExecutorType.Activate); + } + private bool Raidraptor_ForceStrixSummon() + { + return xyz_mode && BeforeResult(ExecutorType.Summon); + } + + private bool Blackwing_ZephyrostheEliteEffect_2() + { + if (!xyz_mode && Bot.GetMonstersInMainZone().Count > 4) return false; + List cards = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.Onfield, true), card => { + return !IsNoLinkCards(card) && !card.IsExtraCard() && !(card.Location == CardLocation.SpellZone + && Func.IsCode(card, CardId.SaryujaSkullDread, CardId.MechaPhantomBeastAuroradon, CardId.HeavymetalfoesElectrumite, CardId.CrystronHalqifibrax, CardId.Raidraptor_WiseStrix, + CardId.Linkuriboh)); + }); + if (cards.Count <= 0 || (cards.Count < 2 && func.CardsCheckCount(cards, func.HasLevel, 4) == cards.Count)) + { + Blackwing_ZephyrostheElite_activate = true; + return false; + } + cards.Sort((cardA, cardB) => + { + if (cardA.Location != CardLocation.MonsterZone && cardB.Location == CardLocation.MonsterZone) return -1; + if (cardA.Location == CardLocation.MonsterZone && cardB.Location != CardLocation.MonsterZone) return 1; + if (cardA.Location == CardLocation.SpellZone && cardB.Location == CardLocation.SpellZone) + { + if (cardA.IsCode(CardId.OracleofZefra) && !cardB.IsCode(CardId.OracleofZefra)) return -1; + if (!cardA.IsCode(CardId.OracleofZefra) && cardB.IsCode(CardId.OracleofZefra)) return 1; + return 0; + } + if (xyz_mode) + { + if (cardA.Level == 4 && cardB.Level != 4) return 1; + if (cardA.Level != 4 && cardB.Level == 4) return -1; + return CardContainer.CompareCardAttack(cardA, cardB); + } + else + { + return CardContainer.CompareCardAttack(cardA, cardB); + } + }); + Blackwing_ZephyrostheElite_activate = false; + AI.SelectCard(cards); + return BeforeResult(ExecutorType.Activate); + } + public override void OnChaining(int player, ClientCard card) + { + if (card == null) return; + if (player == 1 && Func.IsCode(card, 14558127, 59438930, 94145021, 38814750, 73642296, 97268402)) + enemy_activate = true; + base.OnChaining(player, card); + } + private bool BeforeResult(ExecutorType type) + { + if (type == ExecutorType.Activate) + { + ResetFlag(); + ++activate_count; + } + if (type == ExecutorType.Summon) + { + ++summon_count; + } + return true; + } + private bool GoToEndPhase() + { + if (Duel.Player == 0 && Duel.Turn == 1 && func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), IsNoLinkCards) <= 0 + && activate_count + summon_count < 5 && !enemy_activate) + { + + AI.SendCustomChat((int)CustomMessage.Angry); + return true; + } + return false; + + } + private bool DefaultPActivate() + { + if (PendulumActivate() && Func.IsCode(Card, CardId.Zefraxi_TreasureoftheYangZing, CardId.SecretoftheYangZing)) + { + return Bot.HasInHandOrInSpellZone(CardId.NinePillarsofYangZing) && func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.SpellZone, + true), card => { return Func.IsCode(Card, CardId.Zefraxi_TreasureoftheYangZing, CardId.SecretoftheYangZing); }) <= 0 && BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool Blackwing_ZephyrostheEliteEffect() + { + if (!xyz_mode) return false; + return Blackwing_ZephyrostheEliteEffect_2(); + } + private bool Raidraptor_WiseStrixSummon() + { + if (!xyz_mode) return false; + AI.SelectMaterials(CardId.Raidraptor_ArsenalFalcon, CardId.Blackwing_ZephyrostheElite); + return BeforeResult(ExecutorType.Summon); + } + private bool Raidraptor_WiseStrixEffect() + { + if (ActivateDescription == -1) + { + int count = 0; + if (HasInDeck(CardId.Raidraptor_SingingLanius)) ++count; + if (HasInDeck(CardId.Blackwing_ZephyrostheElite)) ++count; + if (HasInDeck(CardId.Raider_Wing)) ++count; + if (count <= 1) return false; + AI.SelectCard(CardId.Raider_Wing); + return BeforeResult(ExecutorType.Activate); + } + return BeforeResult(ExecutorType.Activate); + } + private bool Raidraptor_ArsenalFalconEffect() + { + if (Card.Location == CardLocation.MonsterZone) + { + AI.SelectCard(CardId.SupremeKingGateZero); + AI.SelectNextCard(new int[] { CardId.Blackwing_ZephyrostheElite, CardId.Raider_Wing, CardId.Raidraptor_SingingLanius }); + return BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool Raidraptor_ArsenalFalconSummon() + { + if (!XyzModeCheck(true)) return false; + var materials_lists = Util.GetXyzMaterials(Bot.MonsterZone, 7, 2, false, + card => { return !card.IsCode(CardId.F_A_DawnDragster) && !card.IsCode(CardId.TheMightyMasterofMagic); }); + if (materials_lists.Count <= 0) return false; + AI.SelectMaterials(materials_lists[0]); + xyz_mode = true; + return BeforeResult(ExecutorType.Summon); + } + private bool Odd_EyesMeteorburstDragonCheck() + { + if (!XyzModeCheck()) return false; + var materials_lists = Util.GetXyzMaterials(Func.MergeList(new List() { Card }, + Func.GetZoneCards(Bot, CardLocation.MonsterZone | CardLocation.PendulumZone)), 7, 2, false, + card => { return !card.IsCode(CardId.F_A_DawnDragster) && !card.IsCode(CardId.TheMightyMasterofMagic); }); + if (materials_lists.Count <= 0) return false; + List pre_materials = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), SecretoftheYangZingCheck); + var materials_sy_lists = Util.GetSynchroMaterials(pre_materials, 7, 1, 1, false, true, null, card => { return !card.IsCode(CardId.MythicalBeastJackalKing) && !card.IsCode(CardId.HeraldoftheArcLight); }); + if (materials_sy_lists.Count <= 0) return false; + Odd_EyesMeteorburstDragon_materials.Clear(); + foreach (var materials in materials_sy_lists) + { + if (func.CardsCheckCount(materials, func.IsCode, CardId.SupremeKingGateZero) > 0) + { + Odd_EyesMeteorburstDragon_materials.AddRange(materials); + return true; + } + } + Odd_EyesMeteorburstDragon_materials.AddRange(materials_sy_lists[0]); + return true; + } + private bool Odd_EyesMeteorburstDragonSummon() + { + if (!Odd_EyesMeteorburstDragonCheck()) return false; + AI.SelectMaterials(Odd_EyesMeteorburstDragon_materials); + return BeforeResult(ExecutorType.Summon); + } + private bool Odd_EyesMeteorburstDragonEffect() + { + AI.SelectCard(CardId.SupremeKingGateZero); + return BeforeResult(ExecutorType.Activate); + } + private bool DDSavantKeplerSummon() + { + if (HasInDeck(CardId.DarkContractwiththGate)) + { + summoned = true; + return BeforeResult(ExecutorType.Summon); + } + return false; + } + // private void DebugCards(string msg, IList cards) + // { + //#if DEBUG + // Logger.DebugWriteLine(cards.Count + msg); + // foreach (var card in cards) + // { + // if (card == null) continue; + // NamedCard namedCard = NamedCard.Get(card.Id); + // if (namedCard == null) continue; + // Logger.DebugWriteLine(msg + namedCard.Name); + // } + //#endif + // } + private bool ServantofEndymionEffect_2() + { + if (Card.Location == CardLocation.SpellZone) return BeforeResult(ExecutorType.Activate); + return false; + } + private bool IsSpsummonPMonster(ClientCard card) + { + return IsZefraScaleAbove(card) || IsZefraScaleBelow(card) || card.Id == CardId.SupremeKingGateZero || card.Id == CardId.ServantofEndymion; + } + private int GetSpellActivateCount() + { + int count = 0; + if (!activate_DragonShrine && func.CardsCheckAny(Bot.Hand, func.IsCode, CardId.DragonShrine) && + (HasInDeck(CardId.FlameBeastoftheNekroz) || HasInDeck(CardId.DestrudotheLostDragon_Frisson) || HasInDeck(CardId.SupremeKingDragonDarkwurm))) ++count; + if (!activate_SpellPowerMastery && func.CardsCheckAny(Bot.Hand, func.IsCode, CardId.SpellPowerMastery) + && (HasInDeck(CardId.TheMightyMasterofMagic) || HasInDeck(CardId.ServantofEndymion))) ++count; + if (func.CardsCheckAny(Bot.Hand, func.IsCode, CardId.DarkContractwiththGate)) ++count; + if (!activate_ZefraProvidence && func.CardsCheckAny(Bot.Hand, func.IsCode, CardId.ZefraProvidence)) + { + if (func.CardsCheckCount(Bot.Hand, func.IsCode, CardId.OracleofZefra) <= 0 && !activate_OracleofZefra + && HasInDeck(CardId.OracleofZefra)) + { + count += 2; + } + else + { + ++count; + } + } + if (!activate_OracleofZefra && func.CardsCheckAny(Bot.Hand, func.IsCode, CardId.OracleofZefra)) ++count; + if (func.CardsCheckAny(Bot.Hand, func.IsCode, CardId.Terraforming) && HasInDeck(CardId.OracleofZefra)) ++count; + if (func.CardsCheckAny(Bot.Hand, func.IsCode, CardId.FoolishBurial)) ++count; + if (func.CardsCheckCount(Bot.Hand, func.HasType, CardType.Pendulum) > 1 && Bot.SpellZone[0] == null && + Bot.SpellZone[4] == null) ++count; + if (!summoned && Bot.HasInHand(CardId.DDSavantKepler) && HasInDeck(CardId.DarkContractwiththGate)) ++count; + return count; + } + private bool ServantofEndymionEffect_3() + { + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool ZefraDivineStrikeEffect() + { + if (Duel.LastChainPlayer == 0) return false; + AI.SelectCard(CardId.RitualBeastTamerZeframpilica, CardId.SatellarknightZefrathuban, CardId.StellarknightZefraxciton, CardId.FlameBeastoftheNekroz, CardId.ShaddollZefracore, + CardId.SecretoftheYangZing, CardId.Zefraxi_TreasureoftheYangZing); + return BeforeResult(ExecutorType.Activate); + } + private bool NinePillarsofYangZingEffect() + { + if (Duel.LastChainPlayer == 0) return false; + List cards = func.CardsIdToClientCards(new List { CardId.SecretoftheYangZing }, Bot.MonsterZone); + cards.AddRange(func.CardsIdToClientCards(new List { CardId.SecretoftheYangZing, CardId.Zefraxi_TreasureoftheYangZing }, Bot.SpellZone)); + AI.SelectCard(cards); + should_destory = true; + return BeforeResult(ExecutorType.Activate); + } + private bool IsActivateBlackwing_ZephyrostheElite() + { + return (Blackwing_ZephyrostheElite_activate || HeavymetalfoesElectrumite_activate) && Func.GetZoneCards(Bot, CardLocation.PendulumZone, true).Count <= 0; + } + private bool PendulumDefaultActivate() + { + return IsActivateBlackwing_ZephyrostheElite() || (checkPActivate() && IsActivateScale()); + } + private bool ServantofEndymionEffect() + { + if (PendulumActivate()) + { + if (IsActivateBlackwing_ZephyrostheElite()) return BeforeResult(ExecutorType.Activate); + if ((!HasInDeck(CardId.TheMightyMasterofMagic) && !HasInDeck(CardId.MythicalBeastJackalKing) || GetSpellActivateCount() < 2)) return false; + return BeforeResult(ExecutorType.Activate); + } + else if (Card.Location == CardLocation.SpellZone) + { + if (func.HasInZone(Bot, CardLocation.Hand | CardLocation.PendulumZone, CardId.Zefraath, true)) + { + return func.CardsCheckAny(Bot.Hand, IsSpsummonPMonster) && BeforeResult(ExecutorType.Activate); + } + return BeforeResult(ExecutorType.Activate); + } + else if (Card.Location == CardLocation.MonsterZone) return BeforeResult(ExecutorType.Activate); + else return false; + } + private bool IsZefraScaleAbove(ClientCard card) + { + return Func.IsCode(card, CardId.StellarknightZefraxciton, CardId.SecretoftheYangZing, CardId.FlameBeastoftheNekroz, CardId.ShaddollZefracore); + } + private bool IsZefraScaleBelow(ClientCard card) + { + return Func.IsCode(card, CardId.RitualBeastTamerZeframpilica, CardId.Zefraxi_TreasureoftheYangZing, CardId.SatellarknightZefrathuban); + } + private bool TerraformingEffect() + { + return Bot.HasInHand(CardId.OracleofZefra) && BeforeResult(ExecutorType.Activate); + } + private bool DDSavantKeplerEffect() + { + if (PendulumActivate()) return false; + return BeforeResult(ExecutorType.Activate); + } + private bool FoolishBurialEffect() + { + return BeforeResult(ExecutorType.Activate); + } + private List GetSynchroMaterials() + { + List materials = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), + card => { return !IsNoLinkCards(card) && !card.HasType(CardType.Link | CardType.Xyz); }); + return materials; + } + private bool DestrudotheLostDragon_FrissonEffect() + { + if (Bot.HasInExtra(CardId.CrystronHalqifibrax)) return BeforeResult(ExecutorType.Activate); + if (!Bot.HasInExtra(CardId.F_A_DawnDragster) && !Bot.HasInExtra(CardId.Odd_EyesMeteorburstDragon)) return false; + List pre_materials = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), card => { return SecretoftheYangZingCheck(card) && !IsNoLinkCards(card) && !card.HasType(CardType.Tuner) && card.Level > 0; }); + if (pre_materials.Count <= 0) return false; + List cards = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), card => + { + return !IsNoLinkCards(card) && card.Level > 0 && !card.HasType(CardType.Tuner); + }); + if (cards.Count <= 0) return false; + AI.SelectCard(cards); + return BeforeResult(ExecutorType.Activate); + } + private bool IsCanSynchroSummon(int level) + { + return func.CardsCheckAny(GetSynchroMaterials(), card => { + return (card.Level + level == 8 + && func.CardsCheckAny(Bot.ExtraDeck, synchro_card => { return Func.IsCode(synchro_card, CardId.BorreloadSavageDragon, CardId.PSY_FramelordOmega); })) + || (card.Level + level == 7 && SecretoftheYangZingCheck(card) && func.CardsCheckAny(Bot.ExtraDeck, synchro_card => { return Func.IsCode(synchro_card, CardId.Odd_EyesMeteorburstDragon, CardId.F_A_DawnDragster); })) + || (card.Level + level == 5 && Bot.HasInExtra(CardId.Denglong_FirstoftheYangZing)) + || (card.Level + level == 4 && Bot.HasInExtra(CardId.HeraldoftheArcLight)); + }); + } + private bool DDLamiaEffect() + { + if (Bot.HasInExtra(CardId.MechaPhantomBeastAuroradon) && Bot.GetMonstersInMainZone().Count >= 3) return false; + if (!Bot.HasInExtra(CardId.CrystronHalqifibrax) && !IsCanSynchroSummon(Card.Level)) return false; + AI.SelectCard(CardId.DarkContractwiththGate, CardId.DDSavantKepler); + activate_DDLamia = true; + return true; + } + private bool DragonShrineEffect() + { + return BeforeResult(ExecutorType.Activate); + } + private bool ZefraathEffect() + { + if (PendulumActivate()) return !activate_p_Zefraath || IsActivateBlackwing_ZephyrostheElite(); + if (Card.Location == CardLocation.SpellZone) + { + activate_p_Zefraath = true; + return BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool RitualBeastTamerZeframpilicaEffect() + { + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + return BeforeResult(ExecutorType.Activate); + } + private bool BorreloadSavageDragonSummon_2() + { + if (xyz_mode) return BorreloadSavageDragonSummon(); + return false; + } + private bool BorreloadSavageDragonSummon() + { + var materials_lists = Util.GetSynchroMaterials(Bot.MonsterZone, Card.Level, 1, 1, false, true, null, + card => { return !card.IsCode(CardId.F_A_DawnDragster) && !card.IsCode(CardId.TheMightyMasterofMagic) && !card.IsCode(CardId.HeraldoftheArcLight); }); + if (materials_lists.Count <= 0) return false; + foreach (var materials in materials_lists) + { + if (func.CardsCheckAny(materials, func.IsCode, CardId.MechaPhantomBeastToken)) + { + AI.SelectMaterials(materials); + return BeforeResult(ExecutorType.Summon); + } + } + AI.SelectMaterials(materials_lists[0]); + return BeforeResult(ExecutorType.Summon); + } + private bool BorreloadSavageDragonEffect() + { + /* + * effect1 bug: Unable to read card info in the func "OnselectCard" + * Unable to run in the "ActivateDescription == -1" + */ + AI.SelectCard(new[] + { + CardId.SaryujaSkullDread, + CardId.MechaPhantomBeastAuroradon, + CardId.HeavymetalfoesElectrumite, + CardId.CrystronHalqifibrax, + CardId.Raidraptor_WiseStrix + }); + return BeforeResult(ExecutorType.Activate); + } + private bool TheMightyMasterofMagicEffect() + { + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + else if (Card.Location == CardLocation.MonsterZone) + { + if (Duel.LastChainPlayer == 0) return false; + AI.SelectCard(CardId.ServantofEndymion, CardId.TheMightyMasterofMagic); + return BeforeResult(ExecutorType.Activate); + } + else + { + return BeforeResult(ExecutorType.Activate); + } + } + private bool checkPActivate() + { + if (p_summoned) return false; + if (func.HasInZone(Bot, CardLocation.PendulumZone, CardId.Zefraath, true)) return true; + if (Bot.HasInHand(CardId.Zefraath) && (Bot.SpellZone[0] != null || Bot.SpellZone[4] != null)) return false; + if (Bot.SpellZone[0] == null && Bot.SpellZone[4] == null) + { + if (!Bot.HasInHand(CardId.Zefraath) && !func.CardsCheckAny(Bot.Hand, card => { + return IsSpsummonPMonster(card) && +(Card.LScale >= 5 ? card.LScale < 5 : card.LScale > 5) && func.GetPSpSummonMonster(Bot, card, Card)?.Count > 0; + })) return false; + } + else + { + if (func.GetPSpSummonMonster(Bot, Bot.SpellZone[0], Card)?.Count <= 0 && func.GetPSpSummonMonster(Bot, Bot.SpellZone[4], Card)?.Count <= 0) return false; + } + + return true; + } + private bool SecretoftheYangZingEffect() + { + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + activate_SecretoftheYangZing = true; + return BeforeResult(ExecutorType.Activate); + } + private bool SatellarknightZefrathubanEffect() + { + + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool BorreloadSavageDragonEffect_2() + { + if (Duel.LastChainPlayer == 1) + { + ClientCard card = Util.GetLastChainCard(); + return card != null && !card.HasType(CardType.Continuous | CardType.Field) && card.HasType(CardType.Spell | CardType.Trap) && BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool Zefraxi_TreasureoftheYangZingEffect() + { + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + return BeforeResult(ExecutorType.Activate); + } + private bool OracleofZefraEffect() + { + activate_OracleofZefra = true; + return BeforeResult(ExecutorType.Activate); + + } + private bool FlameBeastoftheNekrozEffect() + { + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + else + { + return BeforeResult(ExecutorType.Activate); + } + + } + private bool AstrographSorcererEffect() + { + if (PendulumActivate()) return false; + return BeforeResult(ExecutorType.Activate); + } + private bool StellarknightZefraxcitonEffect() + { + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + else + { + return BeforeResult(ExecutorType.Activate); + } + } + private bool IsNoLinkCards(ClientCard card) + { + if (card == null) return false; + return ((card.IsCode(CardId.MythicalBeastJackalKing) || card.IsCode(CardId.TheMightyMasterofMagic)) && !card.IsDisabled()) + || card.IsCode(CardId.BorreloadSavageDragon) || card.IsCode(CardId.PSY_FramelordOmega) || card.IsCode(CardId.F_A_DawnDragster) + || card.IsCode(CardId.TruKingofAllCalamities) || card.IsCode(CardId.HeraldoftheArcLight) || card.LinkCount >= 3; + } + private bool LinkuribohSummon() + { + List materials = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), func.HasLevel, 1); + if (func.CardsCheckCount(materials, Func.NegateFunc(func.HasType), CardType.Tuner) <= 0 && + func.CardsCheckCount(materials, func.HasType, CardType.Tuner) <= 1) return false; + materials.Sort((cardA, cardB) => + { + if (cardA.HasType(CardType.Tuner) && !cardB.HasType(CardType.Tuner)) return 1; + if (!cardA.HasType(CardType.Tuner) && cardB.HasType(CardType.Tuner)) return -1; + return 0; + }); + AI.SelectMaterials(materials); + return true; + } + private bool SpellSet() + { + if (Card.HasType(CardType.Trap)) + { + AI.SelectPlace(Zones.z1 | Zones.z2 | Zones.z3 | Zones.z4 | Zones.z0); + return true; + } + return false; + } + private bool SpellSet_2() + { + if (Card.HasType(CardType.QuickPlay)) + { + AI.SelectPlace(Zones.z1 | Zones.z2 | Zones.z3 | Zones.z4 | Zones.z0); + return true; + } + return false; + } + private bool ShaddollZefracoreEffect() + { + if (PendulumActivate()) + { + return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + } + else + { + return BeforeResult(ExecutorType.Activate); + } + } + private bool PSY_FramelordOmegaEffect() + { + if (Card.Location == CardLocation.MonsterZone) + { + if (Duel.Player == 0) return BeforeResult(ExecutorType.Activate); + else + { + if (Bot.Banished.Count <= 0) return false; + AI.SelectCard(func.CardsIdToClientCards(new List { CardId.JetSynchron, CardId.DDLamia }, Bot.Banished)); + return BeforeResult(ExecutorType.Activate); + } + } + else + { + if (Bot.Graveyard.Count <= 0) return false; + AI.SelectCard(func.CardsIdToClientCards(new List { CardId.Zefraath, CardId.CrystronHalqifibrax, CardId.Denglong_FirstoftheYangZing, CardId.BorreloadSavageDragon, CardId.DDLamia }, Bot.Graveyard)); + return BeforeResult(ExecutorType.Activate); + } + } + private bool Psummon() + { + //if (ActivateDescription == P_SPSUMMON_DESC) + if (Card.Location == CardLocation.SpellZone) + { + p_summoning = true; + p_summoned = true; + return true; + } + return false; + } + private bool IsExtraZoneCard(ClientCard card) + { + if (card == null) return false; + ClientCard ex_card = Bot.MonsterZone[5]; + if (ex_card == card) return true; + ex_card = Bot.MonsterZone[6]; + if (ex_card == card) return true; + return false; + } + private bool HeavymetalfoesElectrumiteSummon() + { + if (Odd_EyesMeteorburstDragonCheck()) return false; + List materials = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), func.HasType, CardType.Pendulum); + if (materials.Count > 0) + { + materials.Sort((cardA, cardB) => + { + if ((cardA.Level == 3 || cardA.HasType(CardType.Tuner)) && cardB.Level != 3 && !cardB.HasType(CardType.Tuner)) return -1; + if (cardA.Level != 3 && !cardA.HasType(CardType.Tuner) && (cardB.Level == 3 || cardB.HasType(CardType.Tuner))) return 1; + return CardContainer.CompareCardLevel(cardA, cardB); + }); + materials.Reverse(); + List result = new List(); + foreach (var material in materials) + { + if (IsExtraZoneCard(material)) result.Insert(0, material); + else if (IsNoLinkCards(material) || (material.HasType(CardType.Tuner) && Bot.HasInExtra(CardId.CrystronHalqifibrax) + && func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), func.HasType, CardType.Tuner) <= 0)) continue; + else result.Add(material); + } + if (result.Count < 2) return false; + AI.SelectMaterials(result); + return BeforeResult(ExecutorType.Summon); + } + return false; + } + private bool SecretoftheYangZingCheck(ClientCard card) + { + if (card.IsCode(CardId.SecretoftheYangZing) && Bot.HasInHandOrInSpellZone(CardId.NinePillarsofYangZing)) + { + return func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.Hand | CardLocation.PendulumZone | CardLocation.MonsterZone, true), p_card => { + return p_card.HasSetcode(0xc4) && p_card.HasType(CardType.Pendulum); + }) <= 0; + } + return true; + } + private bool F_A_DawnDragsterSummon() + { + List pre_materials = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), SecretoftheYangZingCheck); + var materials_lists = Util.GetSynchroMaterials(pre_materials, 7, 1, 1, false, true, null, card => { return !card.IsCode(CardId.MythicalBeastJackalKing) && !card.IsCode(CardId.HeraldoftheArcLight); }); + if (materials_lists.Count <= 0) return false; + foreach (var materials in materials_lists) + { + if (func.CardsCheckCount(materials, card => + { + return card.HasType(CardType.Tuner) && card.HasRace(CardRace.Machine); + }) <= 0) + { + AI.SelectMaterials(materials); + return BeforeResult(ExecutorType.Summon); + } + } + AI.SelectMaterials(materials_lists[0]); + return BeforeResult(ExecutorType.Summon); + } + private bool CrystronHalqifibraxEffect() + { + return BeforeResult(ExecutorType.Activate); + } + private bool MechaPhantomBeastAuroradonSummon() + { + if (Bot.GetMonstersInMainZone().Count >= 4 || (!HasInDeck(CardId.MechaPhantomBeastO_Lion) && !IsCanSPSummonTunerLevel1() + && !func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.MonsterZone | CardLocation.Grave, true), func.IsCode, CardId.Deskbot001))) return false; + if (XyzModeCheck()) + { + List pre_materials = new List(); + List key_materials = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.Grave), card => { + return (card.IsCode(CardId.DDLamia) && !activate_DDLamia && func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.Onfield | CardLocation.Hand, true), scard => + { return Func.HasSetCode(scard, 0xaf, 0xae) && scard.Id != CardId.DDLamia; }) > 0); + }); + List key_materials_2 = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.Grave), card => { return card.IsCode(CardId.JetSynchron) && !activate_JetSynchron; }); + pre_materials.AddRange(key_materials); + pre_materials.AddRange(key_materials_2); + if (!summoned) pre_materials.AddRange(func.CardsCheckWhere(Bot.Hand, card => { return !card.IsCode(CardId.DestrudotheLostDragon_Frisson) && card.Level < 5; })); + pre_materials.AddRange(Bot.MonsterZone); + var synchro_materials_lists = Util.GetSynchroMaterials(pre_materials, 7, 1, 1, false, true, null, card => { return !card.IsCode(CardId.MythicalBeastJackalKing); }); + var xyz_materials_lists = Util.GetXyzMaterials(Func.GetZoneCards(Bot, CardLocation.MonsterZone | CardLocation.PendulumZone), 7, 1, false, + card => { return !card.IsCode(CardId.F_A_DawnDragster) && !card.IsCode(CardId.TheMightyMasterofMagic); }); + var xyz_materials_lists_2 = Util.GetXyzMaterials(Func.GetZoneCards(Bot, CardLocation.MonsterZone), 7, 2, false, + card => { return !card.IsCode(CardId.F_A_DawnDragster) && !card.IsCode(CardId.TheMightyMasterofMagic); }); + if ((synchro_materials_lists.Count > 0 && xyz_materials_lists.Count > 0) || xyz_materials_lists_2.Count > 0) return false; + } + List m = new List(); + int link_count = 0; + List cards = Bot.GetMonsters(); + cards.Sort(CardContainer.CompareCardLink); + cards.Reverse(); + foreach (var card in Bot.GetMonsters()) + { + if (card == null) continue; + if (card.IsFacedown() || !card.HasRace(CardRace.Machine) || IsNoLinkCards(card)) continue; + m.Add(card); + link_count += (card.HasType(CardType.Link)) ? card.LinkCount : 1; + if (link_count >= 3) break; + } + if (link_count < 3) return false; + AI.SelectMaterials(m); + return true; + } + private bool SaryujaSkullDreadEffect() + { + if (ActivateDescription == Util.GetStringId(CardId.SaryujaSkullDread, 2)) + { + AI.SelectCard(GetSendToDeckIds()); + return BeforeResult(ExecutorType.Activate); + } + return BeforeResult(ExecutorType.Activate); + } + private bool SaryujaSkullDreadSummon() + { + if (Bot.GetMonstersInMainZone().Count < 4 || (!Bot.HasInExtra(CardId.CrystronHalqifibrax) && !xyz_mode)) return false; + List materials = new List(); + int link_count = 0; + int materials_count = 0; + int tuner_count = func.CardsCheckCount(Bot.MonsterZone, func.HasType, CardType.Tuner); + List temp_materials = Bot.GetMonsters(); + temp_materials.Sort((cardA, cardB) => + { + if ((cardA.HasType(CardType.Tuner) && cardB.HasType(CardType.Tuner)) + || (!cardA.HasType(CardType.Tuner) && !cardB.HasType(CardType.Tuner))) + { + return CardContainer.CompareCardLevel(cardA, cardB); + } + else if (cardA.HasType(CardType.Tuner) && !cardB.HasType(CardType.Tuner)) return 1; + return -1; + }); + foreach (var material in temp_materials) + { + ++materials_count; + if (IsExtraZoneCard(material)) materials.Insert(0, material); + else if (IsNoLinkCards(material)) { --materials_count; continue; } + else materials.Add(material); + link_count += material.HasType(CardType.Link) ? material.LinkCount : 1; + if (link_count >= 4) + { + if (materials_count == 3 && Bot.Deck.Count > 4 && ((func.CardsCheckCount(Bot.Hand, func.HasType, CardType.Tuner) > 0 + || (Bot.HasInMonstersZone(CardId.DDLamia, false, false, true) && !activate_DDLamia && func.CardsCheckCount(Func.GetZoneCards + (Bot, CardLocation.Onfield | CardLocation.Hand, true), card => { return Func.HasSetCode(card, 0xaf, 0xae) && card.Id != CardId.DDLamia; }) + > 0) || (Bot.HasInMonstersZone(CardId.JetSynchron, false, false, true) && !activate_JetSynchron)) || xyz_mode)) + { + --link_count; + continue; + } + break; + } + } + if (materials.Count < 3) return false; + AI.SelectMaterials(materials); + return BeforeResult(ExecutorType.Summon); + } + private bool CrystronHalqifibraxSummon() + { + List materials = new List(); + if (Bot.HasInExtra(CardId.MechaPhantomBeastAuroradon)) + { + materials.Add(Bot.MonsterZone[5]); + materials.Add(Bot.MonsterZone[6]); + } + List mainMonsters = Bot.GetMonstersInMainZone(); + mainMonsters.Sort(CardContainer.CompareCardAttack); + materials.AddRange(mainMonsters); + AI.SelectMaterials(materials); + if (materials.Distinct().Count() <= 3) + { + AI.SendCustomChat((int)CustomMessage.Surprise); + } + return true; + } + private bool PendulumActivate() + { + return Func.PendulumActivate((int)ActivateDescription, Card); + } + private bool IsActivateScale() + { + return func.IsActivateScale(Bot, Card); + } + private bool SpellActivate() + { + return Func.SpellActivate(Card); + } + private bool SupremeKingGateZeroEffect() + { + if (PendulumActivate()) return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + return false; + } + private bool MythicalBeastJackalKingEffect() + { + if (PendulumActivate()) return PendulumDefaultActivate() && BeforeResult(ExecutorType.Activate); + return BeforeResult(ExecutorType.Activate); + } + private bool Denglong_FirstoftheYangZingSummon_2() + { + if (xyz_mode) return Denglong_FirstoftheYangZingSummon(); + return false; + } + private bool Denglong_FirstoftheYangZingSummon() + { + var materials_lists = Util.GetSynchroMaterials(Bot.MonsterZone, 5, 1, 1, false, true, null, + card => { return !card.IsCode(CardId.HeraldoftheArcLight); }); + if (materials_lists.Count <= 0) return false; + AI.SelectMaterials(materials_lists[0]); + return BeforeResult(ExecutorType.Summon); + } + private bool Denglong_FirstoftheYangZingEffect() + { + if (ActivateDescription == Util.GetStringId(CardId.Denglong_FirstoftheYangZing, 1)) return false; + if (Card.Location == CardLocation.MonsterZone) + { + AI.SelectCard(CardId.NinePillarsofYangZing, CardId.SecretoftheYangZing, CardId.Zefraxi_TreasureoftheYangZing); + } + else + { + AI.SelectCard(CardId.SecretoftheYangZing, CardId.Zefraxi_TreasureoftheYangZing, CardId.LightoftheYangZing); + } + return true; + } + private bool DarkContractwiththGateEffect() + { + if (SpellActivate()) + { + return (HasInDeck(CardId.DDLamia) || func.HasInZone(Bot, CardLocation.PendulumZone, CardId.ServantofEndymion, true, true)) && BeforeResult(ExecutorType.Activate); + } + return BeforeResult(ExecutorType.Activate); + } + private int DisabledSort(ClientCard cardA, ClientCard cardB) + { + bool RitualBeastTamerZeframpilica_flag = !summoned && Bot.HasInExtra(CardId.HeraldoftheArcLight) && IsCanSPSummonTunerLevel1(); + if (((cardA.IsCode(CardId.RitualBeastTamerZeframpilica) && RitualBeastTamerZeframpilica_flag) || Func.IsCode(cardA, CardId.MaxxC, CardId.Zefraath, CardId.MythicalBeastJackalKing, CardId.TheMightyMasterofMagic) || cardA.HasType(CardType.Trap) || cardA.HasType(CardType.Tuner)) + && !cardB.IsCode(CardId.RitualBeastTamerZeframpilica) && !cardB.HasType(CardType.Trap) && !Func.IsCode(cardB, CardId.MaxxC, CardId.Zefraath, CardId.MythicalBeastJackalKing, CardId.TheMightyMasterofMagic) && !cardB.HasType(CardType.Tuner)) return 1; + else if (!cardA.IsCode(CardId.RitualBeastTamerZeframpilica) && !cardA.HasType(CardType.Trap) && !Func.IsCode(cardA, CardId.MaxxC, CardId.Zefraath, CardId.MythicalBeastJackalKing, CardId.TheMightyMasterofMagic) && !cardA.HasType(CardType.Tuner) + && ((cardB.IsCode(CardId.RitualBeastTamerZeframpilica) && RitualBeastTamerZeframpilica_flag) || Func.IsCode(cardB, CardId.MaxxC, CardId.Zefraath, CardId.MythicalBeastJackalKing, CardId.TheMightyMasterofMagic) || cardB.HasType(CardType.Trap) || cardB.HasType(CardType.Tuner))) return -1; + return 0; + } + private List GetDisabledIds() + { + List ids = new List(); + ids.Add(CardId.DestrudotheLostDragon_Frisson); + ids.Add(CardId.Blackwing_ZephyrostheElite); + ids.Add(CardId.Raider_Wing); + ids.Add(CardId.Raidraptor_SingingLanius); + ids.Add(CardId.PSY_FrameDriver); + if (!Bot.HasInGraveyard(CardId.Raidraptor_ArsenalFalcon) || !Bot.HasInExtra(CardId.TruKingofAllCalamities)) ids.Add(CardId.Rank_Up_MagicSoulShaveForce); + if (Bot.HasInBanished(CardId.PSY_FrameDriver)) ids.Add(CardId.PSY_FramegearGamma); + ids.Add(CardId.LightoftheYangZing); + ids.Add(CardId.DDLamia); + ids.AddRange(Func.GetCardsRepeatCardsId(Bot.Hand)); + List hands = Func.GetZoneCards(Bot, CardLocation.Hand); + hands.Sort(DisabledSort); + List hand_ids = Func.ClientCardsToCardsId(hands, true); + ids.AddRange(hand_ids); + return ids; + } + private List GetSendToDeckIds() + { + List ids = new List(); + List repeat_ids = Func.GetCardsRepeatCardsId(Func.GetZoneCards(Bot, CardLocation.Hand)); + ids.Add(CardId.MechaPhantomBeastO_Lion); + ids.AddRange(repeat_ids); + ids.Add(CardId.Raidraptor_SingingLanius); + ids.Add(CardId.Raider_Wing); + ids.Add(CardId.Blackwing_ZephyrostheElite); + ids.Add(CardId.PSY_FrameDriver); + ids.Add(CardId.LightoftheYangZing); + ids.Add(CardId.Rank_Up_MagicSoulShaveForce); + if (activate_ZefraProvidence) ids.Add(CardId.ZefraProvidence); + if (activate_OracleofZefra) ids.Add(CardId.OracleofZefra); + if (activate_DragonShrine) ids.Add(CardId.DragonShrine); + if (activate_SpellPowerMastery) ids.Add(CardId.SpellPowerMastery); + List hands = Func.GetZoneCards(Bot, CardLocation.Hand); + hands.Sort(DisabledSort); + List hand_ids = Func.ClientCardsToCardsId(hands, true); + ids.AddRange(hand_ids); + return ids; + } + private bool TruKingofAllCalamitiesEffect() + { + if (Duel.Player == 1) + { + AI.SelectAttributes(new CardAttribute[] { CardAttribute.Divine }); + return BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool JetSynchronEffect() + { + + if (Card.Location == CardLocation.Grave) + { + if (!IsCanSynchroSummon(Card.Level)) return false; + if (func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.Extra), card => { return card.HasType(CardType.Synchro) || Func.IsCode(card, CardId.CrystronHalqifibrax, CardId.Linkuriboh); })) + { + activate_JetSynchron = true; + List dcards = func.CardsIdToClientCards(GetDisabledIds(), Bot.Hand); + if (!Bot.HasInExtra(CardId.CrystronHalqifibrax) && dcards.Count <= 0) return false; + AI.SelectCard(dcards); + return BeforeResult(ExecutorType.Activate); + } + } + return false; + } + private bool MechaPhantomBeastAuroradonEffect() + { + if (ActivateDescription == -1) { link_summoned = true; return true; } + else + { + if (!HasInDeck(CardId.MechaPhantomBeastO_Lion) + && Func.GetZoneCards(Enemy, CardLocation.Onfield).Count <= 0) return false; + List tRelease = new List(); + List nRelease = new List(); + foreach (var card in Bot.GetMonsters()) + { + if (card == null || IsNoLinkCards(card)) continue; + if (card.Id == CardId.MechaPhantomBeastToken) tRelease.Add(card); else nRelease.Add(card); + } + int count = tRelease.Count() + nRelease.Count(); + opt_0 = false; + opt_1 = false; + opt_2 = false; + if (count >= 3 && func.CardsCheckCount(Bot.Graveyard, func.HasType, CardType.Trap) > 0) opt_2 = true; + if (count >= 2 && CheckRemainInDeck(CardId.MechaPhantomBeastO_Lion) > 0) opt_1 = true; + if (count >= 1 && Func.GetZoneCards(Enemy, CardLocation.Onfield).Count > 0) opt_0 = true; + if (!opt_0 && !opt_1 && !opt_2) return false; + return true; + } + } + private bool SupremeKingDragonDarkwurmEffect() + { + if (Card.Location == CardLocation.Grave) + { + activate_SupremeKingDragonDarkwurm_1 = true; + return BeforeResult(ExecutorType.Activate); + } + if (Card.Location == CardLocation.MonsterZone) + { + activate_SupremeKingDragonDarkwurm_2 = true; + return BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool SupremeKingDragonDarkwurmSummon() + { + if ((!activate_p_Zefraath && Bot.HasInHand(CardId.Zefraath) && !activate_SupremeKingDragonDarkwurm_1 && HasInDeck(CardId.SupremeKingGateZero) && func.CardsCheckAny(Bot.Hand, func.HasType, CardType.Tuner)) + || (func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.Hand), card => { return card.LinkCount > 5; }) && + !Bot.HasInHand(CardId.SupremeKingGateZero) && !activate_SupremeKingDragonDarkwurm_2)) + { + summoned = true; + return BeforeResult(ExecutorType.Summon); + } + return false; + } + private bool DefaultSummon_2() + { + if (Card.Location == CardLocation.Hand && Card.Level <= 4 + && Bot.HasInExtra(CardId.CrystronHalqifibrax) && (func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), card => { return Card.HasType(CardType.Tuner) ? true : card.HasType(CardType.Tuner); }))) + { + summoned = true; + return BeforeResult(ExecutorType.Summon); + } + return false; + } + private bool IsCanSPSummonTunerLevel1() + { + return func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.MonsterZone | CardLocation.Grave, true), card => { + return (card.IsCode(CardId.DDLamia) && !activate_DDLamia && func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.Onfield | CardLocation.Hand, true), scard => { return Func.HasSetCode(scard, 0xaf, 0xae) && scard.Id != CardId.DDLamia; }) + > 0) || (card.IsCode(CardId.JetSynchron) && !activate_JetSynchron) && Bot.GetMonstersInMainZone().Count <= 3; + }); + } + private bool DefaultSummon() + { + if (Card.Level > 4) return false; + if ((!link_summoned && Bot.HasInExtra(CardId.HeavymetalfoesElectrumite) && func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), func.HasType, CardType.Pendulum) > 0 + && Card.HasType(CardType.Pendulum)) + || (IsCanSPSummonTunerLevel1() && ((Card.Level == 3 && Bot.HasInExtra(CardId.HeraldoftheArcLight)) || ( + Card.Level == 4 && Bot.HasInExtra(CardId.Denglong_FirstoftheYangZing)))) || + (Card.Id == CardId.SupremeKingDragonDarkwurm && !activate_SupremeKingDragonDarkwurm_2) + || (Bot.HasInExtra(CardId.CrystronHalqifibrax) && Bot.HasInHandOrInGraveyard(CardId.DestrudotheLostDragon_Frisson) && !activate_DestrudotheLostDragon_Frisson)) + { + summoned = true; + return BeforeResult(ExecutorType.Summon); + } + return false; + } + private bool OneforOneEffect() + { + AI.SelectCard(GetDisabledIds()); + AI.SelectNextCard(CardId.JetSynchron, CardId.LightoftheYangZing, CardId.DDLamia); + return BeforeResult(ExecutorType.Activate); + } + private void HeavymetalfoesElectrumiteAddIds(List ids) + { + if (!summoned && HasInDeck(CardId.DarkContractwiththGate) && HasInDeck(CardId.DDLamia)) + { + if (!func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), func.HasType, CardType.Tuner)) + { + ids.Add(CardId.DDSavantKepler); + } + else + { + ids.Add(CardId.AstrographSorcerer); + ids.Add(CardId.DDSavantKepler); + } + } + ids.Add(CardId.AstrographSorcerer); + ids.Add(CardId.FlameBeastoftheNekroz); + ids.Add(CardId.DDSavantKepler); + } + private bool LinkuribohEffect() + { + if (Card.Location == CardLocation.Grave) + { + if (Duel.Player != 0) return BeforeResult(ExecutorType.Activate); + return false; + } + return BeforeResult(ExecutorType.Activate); + } + private bool Raider_WingEffect() + { + if (!Bot.HasInMonstersZone(CardId.Raidraptor_ForceStrix, false, true, true)) return false; + AI.SelectCard(CardId.Raidraptor_ForceStrix); + return BeforeResult(ExecutorType.Activate); + } + private bool HeavymetalfoesElectrumiteEffect() + { + if (ActivateDescription != -1) + { + List cards = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.SpellZone, true), Func.NegateFunc(func.IsCode), CardId.DarkContractwiththGate); + if (cards.Count <= 0) + { + HeavymetalfoesElectrumite_activate = true; + return false; + } + HeavymetalfoesElectrumite_activate = false; + return BeforeResult(ExecutorType.Activate); + } + return BeforeResult(ExecutorType.Activate); + } + public override bool OnSelectHand() + { + return true; + } + private bool ResetFlag() + { + should_destory = false; + return true; + } + private bool HeraldoftheArcLightEffect() + { + if (Card.Location == CardLocation.MonsterZone) + { + return Duel.LastChainPlayer != 0 && BeforeResult(ExecutorType.Activate); + } + return false; + } + private bool CalledbytheGraveEffect() + { + if ((Bot.SpellZone[5] == Card || Bot.SpellZone[0] == Card) && Duel.Player == 0) return BeforeResult(ExecutorType.Activate); + ClientCard card = Util.GetLastChainCard(); + if (card == null) return false; + int id = card.Id; + List g_cards = func.CardsCheckWhere(Enemy.Graveyard, func.IsCode, id); + if (Duel.LastChainPlayer != 0) + { + if (card.Location == CardLocation.Grave && card.HasType(CardType.Monster)) + { + AI.SelectCard(card); + return BeforeResult(ExecutorType.Activate); + } + else if (g_cards.Count() > 0 && card.HasType(CardType.Monster)) + { + AI.SelectCard(g_cards); + return BeforeResult(ExecutorType.Activate); + } + } + return false; + } + public override CardPosition OnSelectPosition(int cardId, IList positions) + { + NamedCard card = NamedCard.Get(cardId); + if (cardId == CardId.F_A_DawnDragster && Duel.Turn > 1) return CardPosition.FaceUpAttack; + if (card.Attack <= 1000) return CardPosition.FaceUpDefence; + return base.OnSelectPosition(cardId, positions); + } + public override int OnSelectOption(IList options) + { + if (options.Contains(Util.GetStringId(CardId.MechaPhantomBeastAuroradon, 3))) + { + if (opt_1) return options.IndexOf(Util.GetStringId(CardId.MechaPhantomBeastAuroradon, 3)); + else if (opt_0) return 0; + return (int)options[options.Count - 1]; + } + return base.OnSelectOption(options); + } + + public override int OnSelectPlace(long cardId, int player, CardLocation location, int available) + { + NamedCard card = NamedCard.Get(cardId); + if (player == 0) + { + if (location == CardLocation.SpellZone) + { + if (card.HasType(CardType.Pendulum)) + { + if ((available & Zones.z4) > 0) return Zones.z4; + if ((available & Zones.z0) > 0) return Zones.z0; + } + else + { + List keys = new List() { 1, 2, 3 }; + while (keys.Count > 0) + { + int index = Program.Rand.Next(keys.Count); + int key = keys[index]; + int zone = 1 << key; + if ((zone & available) > 0) return zone; + keys.Remove(key); + } + } + } + else if (location == CardLocation.MonsterZone) + { + if (card.HasType(CardType.Link)) + { + if ((available & Zones.z5) > 0) return Zones.z5; + if ((available & Zones.z6) > 0) return Zones.z6; + } + } + } + + return base.OnSelectPlace(cardId, player, location, available); + } + private IList _OnSelectPendulumSummon(IList cards, int min, int max) + { + List ids = func.GetSelectCardIdList(); + List result = func.GetSelectCardList(); + List exs = func.CardsCheckWhere(cards, func.IsLocation, CardLocation.Extra); + List hs = func.CardsCheckWhere(cards, Func.NegateFunc(func.IsLocation), CardLocation.Extra); + if (func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.PendulumZone, true), card => { + return card.HasSetcode(0xc4) && !card.IsCode(CardId.Zefraath); + }) && func.CardsCheckAny(exs, func.IsCode, CardId.ShaddollZefracore)) ids.Add(CardId.ShaddollZefracore); + result = func.CardsIdToClientCards(ids, cards); + List temp_cards = func.CardsCheckWhere(cards, Func.NegateFunc(func.IsCode), CardId.MaxxC); + result.AddRange(temp_cards); + if (result.Count <= 0) return Func.CheckSelectCount(Util, result, cards, min, min); + if (result[0] != null && result[0].Location != CardLocation.Extra) ++p_count; + return Func.CheckSelectCount(Util, result, cards, max, max); + } + + public override IList OnSelectCard(IList cards, int min, int max, long hint, bool cancelable) + { + if (AI.HaveSelectedCards()) return null; + List ids = func.GetSelectCardIdList(); + List result = func.GetSelectCardList(); + if (hint == HintMsg.AddToHand) + { + if (func.CardsCheckAny(cards, card => { return card.Location == CardLocation.Deck && card.HasSetcode(0xc4); })) + { + if (!activate_ZefraProvidence) ids.Add(CardId.ZefraProvidence); + if (p_summoned) + { + if (!summoned && Bot.HasInExtra(CardId.HeavymetalfoesElectrumite) && func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.MonsterZone, true), func.HasType, CardType.Pendulum) == 1) + { + List pre_ids = new List {CardId.Zefraxi_TreasureoftheYangZing,CardId.StellarknightZefraxciton,CardId.RitualBeastTamerZeframpilica,CardId.NinePillarsofYangZing + ,CardId.StellarknightZefraxciton,CardId.ShaddollZefracore}; + ids.AddRange(pre_ids); + } + ids.Add(CardId.ZefraDivineStrike); + } + if (!activate_OracleofZefra) ids.Add(CardId.OracleofZefra); + if (!activate_p_Zefraath && !func.HasInZone(Bot, CardLocation.Hand | CardLocation.PendulumZone, CardId.Zefraath, true)) ids.Add(CardId.Zefraath); + if (func.HasInZone(Bot, CardLocation.Hand | CardLocation.PendulumZone, CardId.SupremeKingGateZero, true) && !func.CardsCheckAny(Bot.Hand, func.HasType, CardType.Tuner) + && !Bot.HasInHand(CardId.Zefraxi_TreasureoftheYangZing)) ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + List pMonsters = func.CardsCheckWhere(Func.GetZoneCards(Bot, CardLocation.Hand), card => + { + return card.HasType(CardType.Pendulum) && !card.IsCode(CardId.Zefraath); + }); + if (pMonsters.Count > 0) + { + List zefraPMonsters = func.CardsCheckWhere(pMonsters, func.HasSetCode, 0xc4); + if (zefraPMonsters.Count > 0) + { + zefraPMonsters.Sort(Func.CompareCardScale); + int minScale = zefraPMonsters[0].RScale; + if (Bot.HasInHand(CardId.Zefraath)) + { + if (minScale < 5) + { + if (func.CardsCheckCount(cards, IsZefraScaleAbove) > 1) + { + ids.Add(CardId.ShaddollZefracore); + if (!Bot.HasInHand(CardId.Zefraxi_TreasureoftheYangZing)) ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + ids.Add(CardId.SecretoftheYangZing); + ids.Add(CardId.FlameBeastoftheNekroz); + ids.Add(CardId.StellarknightZefraxciton); + ids.Add(CardId.SatellarknightZefrathuban); + ids.Add(CardId.RitualBeastTamerZeframpilica); + } + else + { + ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + ids.Add(CardId.RitualBeastTamerZeframpilica); + ids.Add(CardId.SatellarknightZefrathuban); + } + } + else + { + if (func.CardsCheckCount(cards, IsZefraScaleBelow) > 1) + { + ids.Add(CardId.ShaddollZefracore); + if (!Bot.HasInHand(CardId.Zefraxi_TreasureoftheYangZing)) ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + ids.Add(CardId.SecretoftheYangZing); + ids.Add(CardId.FlameBeastoftheNekroz); + ids.Add(CardId.StellarknightZefraxciton); + ids.Add(CardId.SatellarknightZefrathuban); + ids.Add(CardId.RitualBeastTamerZeframpilica); + } + else + { + ids.Add(CardId.StellarknightZefraxciton); + ids.Add(CardId.SecretoftheYangZing); + ids.Add(CardId.FlameBeastoftheNekroz); + ids.Add(CardId.ShaddollZefracore); + } + } + } + else + { + if (Bot.HasInGraveyard(CardId.FlameBeastoftheNekroz)) ids.Add(CardId.RitualBeastTamerZeframpilica); + ids.Add(CardId.SecretoftheYangZing); + ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + } + } + else + { + ids.Add(CardId.SecretoftheYangZing); + ids.Add(CardId.FlameBeastoftheNekroz); + ids.Add(CardId.StellarknightZefraxciton); + ids.Add(CardId.SatellarknightZefrathuban); + ids.Add(CardId.RitualBeastTamerZeframpilica); + ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + } + } + else + { + if (func.HasInZone(Bot, CardLocation.Hand | CardLocation.PendulumZone, CardId.Zefraath, true) && + !activate_p_Zefraath) + { + ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + ids.Add(CardId.SatellarknightZefrathuban); + ids.Add(CardId.RitualBeastTamerZeframpilica); + } + ids.Add(CardId.SecretoftheYangZing); + ids.Add(CardId.FlameBeastoftheNekroz); + ids.Add(CardId.StellarknightZefraxciton); + ids.Add(CardId.SatellarknightZefrathuban); + ids.Add(CardId.RitualBeastTamerZeframpilica); + ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + } + result = func.CardsIdToClientCards(ids, cards); + } + else if (func.CardsCheckALL(cards, func.IsLocation, true, CardLocation.Extra)) + { + HeavymetalfoesElectrumiteAddIds(ids); + result = func.CardsIdToClientCards(ids, cards); + } + else if (func.CardsCheckALL(cards, func.HasSetCode, true, 0x12a)) + { + if (!func.HasInZone(Bot, CardLocation.PendulumZone | CardLocation.Hand, CardId.ServantofEndymion, true) || + (func.HasInZone(Bot, CardLocation.PendulumZone | CardLocation.Hand, CardId.ServantofEndymion, true) && (!HasInDeck(CardId.TheMightyMasterofMagic) || !HasInDeck(CardId.MythicalBeastJackalKing)))) ids.Add(CardId.ServantofEndymion); + ids.Add(CardId.TheMightyMasterofMagic); + ids.Add(CardId.MythicalBeastJackalKing); + result = func.CardsIdToClientCards(ids, cards); + } + else if (func.CardsCheckALL(cards, func.HasSetCode, true, 0xaf)) + { + ids.Add(CardId.DDLamia); + ids.Add(CardId.DDSavantKepler); + result = func.CardsIdToClientCards(ids, cards); + } + } + else if (hint == HintMsg.ToDeck && func.CardsCheckALL(cards, func.IsLocation, true, CardLocation.Hand) && min == 3 && max == 3) + { + result = func.CardsIdToClientCards(GetSendToDeckIds(), cards); + } + else if (hint == HintMsg.ToGrave && func.CardsCheckALL(cards, func.IsLocation, true, CardLocation.Deck)) + { + List extra_ids = CheckShouldSpsummonExtraMonster(); + //if (func.CardsCheckAny(cards, Func.HasRace, CardRace.Dragon)) + if (extra_ids.Count <= 0) + { + if (!activate_SupremeKingDragonDarkwurm_2 && Bot.GetMonsterCount() <= 0) ids.Add(CardId.SupremeKingDragonDarkwurm); + if (!activate_DestrudotheLostDragon_Frisson) ids.Add(CardId.DestrudotheLostDragon_Frisson); + if (!activate_JetSynchron) ids.Add(CardId.JetSynchron); + ids.Add(CardId.FlameBeastoftheNekroz); + } + else if (extra_ids.Count > 1) + { + if (Bot.GetMonsterCount() <= 0 && !activate_SupremeKingDragonDarkwurm_2) ids.Add(CardId.SupremeKingDragonDarkwurm); + if (func.CardsCheckAny(Bot.Hand, card => { return card.Level < 7 && card.HasType(CardType.Monster); })) ids.Add(CardId.DestrudotheLostDragon_Frisson); + if (Bot.GetHandCount() > 0) ids.Add(CardId.JetSynchron); + if (!summoned && Bot.HasInHand(CardId.RitualBeastTamerZeframpilica)) ids.Add(CardId.FlameBeastoftheNekroz); + ids.Add(CardId.DestrudotheLostDragon_Frisson); + ids.Add(CardId.JetSynchron); + ids.Add(CardId.SupremeKingDragonDarkwurm); + ids.Add(CardId.FlameBeastoftheNekroz); + } + else if (extra_ids.Contains(CardId.HeavymetalfoesElectrumite)) + { + if (Bot.GetMonsterCount() <= 0 && !activate_SupremeKingDragonDarkwurm_2) ids.Add(CardId.SupremeKingDragonDarkwurm); + if (!summoned && Bot.HasInHand(CardId.RitualBeastTamerZeframpilica)) ids.Add(CardId.FlameBeastoftheNekroz); + //if (!summoned && func.CardsCheckAny(Bot.Hand, card => { return card.Level < 7 && card.HasType(CardType.Monster); })) ids.Add(CardId.DestrudotheLostDragon_Frisson); + //if (Bot.GetHandCount() > 0) ids.Add(CardId.JetSynchron); + ids.Add(CardId.DestrudotheLostDragon_Frisson); + ids.Add(CardId.JetSynchron); + ids.Add(CardId.SupremeKingDragonDarkwurm); + ids.Add(CardId.FlameBeastoftheNekroz); + } + else if (extra_ids.Contains(CardId.CrystronHalqifibrax)) + { + if (func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.Hand | CardLocation.MonsterZone, true), func.HasType, CardType.Tuner)) + { + if (Bot.GetMonsterCount() <= 0 && !activate_SupremeKingDragonDarkwurm_2) ids.Add(CardId.SupremeKingDragonDarkwurm); + ids.Add(CardId.DestrudotheLostDragon_Frisson); + ids.Add(CardId.JetSynchron); + ids.Add(CardId.SupremeKingDragonDarkwurm); + ids.Add(CardId.FlameBeastoftheNekroz); + } + else + { + ids.Add(CardId.DestrudotheLostDragon_Frisson); + ids.Add(CardId.JetSynchron); + ids.Add(CardId.SupremeKingDragonDarkwurm); + ids.Add(CardId.FlameBeastoftheNekroz); + + } + } + result = func.CardsIdToClientCards(ids, cards); + } + else if (hint == Util.GetStringId(CardId.Zefraath, 1)) + { + int[] pScales = Func.GetPScales(Bot); + int rScale = pScales[0]; + int lScale = pScales[1]; + int pScale = (rScale != 5) ? rScale : lScale; + if (pScale < 5) + { + if (!activate_SecretoftheYangZing && !func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.Hand | CardLocation.Extra, true), func.IsCode, CardId.SecretoftheYangZing)) ids.Add(CardId.SecretoftheYangZing); + if (!activate_ShaddollZefracore && func.CardsCheckAny(Func.GetZoneCards(Bot, CardLocation.PendulumZone, true), card => { return !card.IsCode(CardId.Zefraath) && card.HasSetcode(0xc4); })) ids.Add(CardId.ShaddollZefracore); + ids.Add(CardId.StellarknightZefraxciton); + ids.Add(CardId.SecretoftheYangZing); + ids.Add(CardId.ShaddollZefracore); + } + else + { + ids.Add(CardId.Zefraxi_TreasureoftheYangZing); + ids.Add(CardId.SatellarknightZefrathuban); + ids.Add(CardId.RitualBeastTamerZeframpilica); + } + result = func.CardsIdToClientCards(ids, cards); + } + else if (hint == Util.GetStringId(CardId.HeavymetalfoesElectrumite, 3)) + { + HeavymetalfoesElectrumiteAddIds(ids); + result = func.CardsIdToClientCards(ids, cards); + } + else if (!(IS_YGOPRO & !(hint == HintMsg.SpSummon)) && func.CardsCheckALL(cards, card => { + return card.IsCode(CardId.TheMightyMasterofMagic) || card.IsCode(CardId.MythicalBeastJackalKing); + }, true)) + { + ids.Add(CardId.MythicalBeastJackalKing); + ids.Add(CardId.TheMightyMasterofMagic); + result = func.CardsIdToClientCards(ids, cards); + } + else if (p_summoning || ((Card == Bot.SpellZone[0] || Card == Bot.SpellZone[4]) && hint == HintMsg.SpSummon && + Card.HasType(CardType.Pendulum))) + { + p_summoning = false; + if (p_count >= 3 && !Bot.HasInExtra(CardId.SaryujaSkullDread) && Bot.HasInExtra(CardId.MechaPhantomBeastAuroradon)) return Func.CheckSelectCount(Util, result, cards, min, min); + return _OnSelectPendulumSummon(cards, min, max); + } + else if (hint == HintMsg.Destroy) + { + if (func.CardsCheckALL(cards, card => { return card.Controller == 0 && card.IsFaceup(); }, true)) + { + should_destory = true; + if (func.CardsCheckALL(cards, func.HasSetCode, true, 0x9e)) + { + if (!activate_SecretoftheYangZing) result = func.CardsIdToClientCards(new List { CardId.SecretoftheYangZing }, func.CardsCheckWhere(cards, + func.IsLocation, CardLocation.MonsterZone)); + result.AddRange(func.CardsIdToClientCards(new List { CardId.SecretoftheYangZing, CardId.Zefraxi_TreasureoftheYangZing }, func.CardsCheckWhere(cards, + Func.NegateFunc(func.IsLocation), CardLocation.MonsterZone))); + } + else + { + List scards = func.CardsCheckWhere(cards, card => { return card.Location == CardLocation.SpellZone; }); + scards.Sort((cardA, cardB) => + { + if (Func.IsCode(cardA, CardId.OracleofZefra, CardId.DarkContractwiththGate) && !Func.IsCode(cardB, CardId.OracleofZefra, CardId.DarkContractwiththGate)) return 1; + if (!Func.IsCode(cardA, CardId.OracleofZefra, CardId.DarkContractwiththGate) && Func.IsCode(cardB, CardId.OracleofZefra, CardId.DarkContractwiththGate)) return -1; + return 0; + }); + result.AddRange(scards); + } + + } + else if (func.CardsCheckAny(cards, card => { return card.Controller == 1 && (card.Location & CardLocation.Onfield) > 0; }) && min == 1 && max == 1) + { + ClientCard card = Util.GetBestEnemyCard(); + if (card != null && cards.Contains(card)) result.Add(card); + else + { + result = new List(func.CardsCheckWhere(cards, ecard => { return ecard.Controller == 1; })); + if (result.Count <= 0) return null; + result.Sort(CardContainer.CompareCardAttack); + result.Reverse(); + } + } + } + else if (hint == HintMsg.SpSummon) + { + List tuner_ids = new List() + { + CardId.DestrudotheLostDragon_Frisson, CardId.PSY_FrameDriver, CardId.JetSynchron, CardId.PSY_FramegearGamma,CardId.LightoftheYangZing + }; + List no_tuner_ids = new List() + { + CardId.TheMightyMasterofMagic, CardId.MythicalBeastJackalKing, CardId.SecretoftheYangZing + }; + if (func.CardsCheckALL(cards, func.IsLocation, true, CardLocation.Hand)) + { + if (summoned && Bot.HasInExtra(CardId.CrystronHalqifibrax) && func.CardsCheckCount(Bot.MonsterZone, card => { + return card.IsFaceup() && card.HasType(CardType.Tuner); + }) <= 0 && !(Bot.HasInGraveyard(CardId.DDLamia) && !activate_DDLamia + && func.CardsCheckCount(Func.GetZoneCards(Bot, CardLocation.Onfield | CardLocation.Hand, true), card => { return Func.HasSetCode(card, 0xaf, 0xae) && card.Id != CardId.DDLamia; }) + <= 0) && !(Bot.HasInGraveyard(CardId.JetSynchron) && !activate_JetSynchron) + && !(Bot.HasInGraveyard(CardId.DestrudotheLostDragon_Frisson) && !activate_DestrudotheLostDragon_Frisson)) + { + ids.AddRange(tuner_ids); + ids.AddRange(no_tuner_ids); + } + else + { + ids.AddRange(no_tuner_ids); + ids.AddRange(tuner_ids); + } + result = func.CardsIdToClientCards(ids, cards); + } + else if (func.CardsCheckALL(cards, card => { + return Func.IsCode(card, CardId.LightoftheYangZing, CardId.PSY_FramegearGamma, + CardId.MechaPhantomBeastO_Lion, CardId.JetSynchron, CardId.Deskbot001, CardId.DDLamia); + })) + { + if (Bot.GetMonstersInMainZone().Count <= 1) ids.Add(CardId.Deskbot001); + ids.Add(CardId.JetSynchron); + ids.Add(CardId.Deskbot001); + ids.Add(CardId.LightoftheYangZing); + ids.Add(CardId.PSY_FramegearGamma); + result = func.CardsIdToClientCards(ids, cards); + } + + } + else if (hint == HintMsg.Release && func.CardsCheckAny(cards, func.IsLocation, CardLocation.MonsterZone)) + { + List tRelease = new List(); + List nRelease = new List(); + foreach (var card in cards) + { + if (card == null || IsNoLinkCards(card)) continue; + if (card.Id == CardId.MechaPhantomBeastToken) tRelease.Add(card); + else if (card.Id == CardId.Raidraptor_WiseStrix) tRelease.Insert(0, card); + else nRelease.Add(card); + } + result.AddRange(tRelease); + result.AddRange(nRelease); + } + IList selectResult = Func.CheckSelectCount(Util, result, cards, min, max); + if (selectResult == null) return base.OnSelectCard(cards, min, max, hint, cancelable); + return selectResult; + } + private bool HasInDeck(int id) + { + return CheckRemainInDeck(id) > 0; + } + private int CheckRemainInDeck(int id) + { + switch (id) + { + case CardId.PSY_FrameDriver: + return Bot.GetRemainingCount(CardId.PSY_FrameDriver, 1); + case CardId.Zefraath: + return Bot.GetRemainingCount(CardId.Zefraath, 3); + case CardId.TheMightyMasterofMagic: + return Bot.GetRemainingCount(CardId.TheMightyMasterofMagic, 1); + case CardId.AstrographSorcerer: + return Bot.GetRemainingCount(CardId.AstrographSorcerer, 1); + case CardId.DestrudotheLostDragon_Frisson: + return Bot.GetRemainingCount(CardId.DestrudotheLostDragon_Frisson, 1); + case CardId.SupremeKingGateZero: + return Bot.GetRemainingCount(CardId.SupremeKingGateZero, 2); + case CardId.MythicalBeastJackalKing: + return Bot.GetRemainingCount(CardId.MythicalBeastJackalKing, 1); + case CardId.SecretoftheYangZing: + return Bot.GetRemainingCount(CardId.SecretoftheYangZing, 3); + case CardId.FlameBeastoftheNekroz: + return Bot.GetRemainingCount(CardId.FlameBeastoftheNekroz, 1); + case CardId.StellarknightZefraxciton: + return Bot.GetRemainingCount(CardId.StellarknightZefraxciton, 1); + case CardId.SupremeKingDragonDarkwurm: + return Bot.GetRemainingCount(CardId.SupremeKingDragonDarkwurm, 1); + case CardId.Blackwing_ZephyrostheElite: + return Bot.GetRemainingCount(CardId.Blackwing_ZephyrostheElite, 1); + case CardId.ShaddollZefracore: + return Bot.GetRemainingCount(CardId.ShaddollZefracore, 1); + case CardId.Raidraptor_SingingLanius: + return Bot.GetRemainingCount(CardId.Raidraptor_SingingLanius, 1); + case CardId.SatellarknightZefrathuban: + return Bot.GetRemainingCount(CardId.SatellarknightZefrathuban, 1); + case CardId.Raider_Wing: + return Bot.GetRemainingCount(CardId.Raider_Wing, 1); + case CardId.Zefraxi_TreasureoftheYangZing: + return Bot.GetRemainingCount(CardId.Zefraxi_TreasureoftheYangZing, 2); + case CardId.RitualBeastTamerZeframpilica: + return Bot.GetRemainingCount(CardId.RitualBeastTamerZeframpilica, 1); + case CardId.ServantofEndymion: + return Bot.GetRemainingCount(CardId.ServantofEndymion, 3); + case CardId.PSY_FramegearGamma: + return Bot.GetRemainingCount(CardId.PSY_FramegearGamma, 3); + case CardId.MechaPhantomBeastO_Lion: + return Bot.GetRemainingCount(CardId.MechaPhantomBeastO_Lion, 1); + case CardId.MaxxC: + return Bot.GetRemainingCount(CardId.MaxxC, 3); + case CardId.Deskbot001: + return Bot.GetRemainingCount(CardId.Deskbot001, 1); + case CardId.JetSynchron: + return Bot.GetRemainingCount(CardId.JetSynchron, 1); + case CardId.DDLamia: + return Bot.GetRemainingCount(CardId.DDLamia, 1); + case CardId.DDSavantKepler: + return Bot.GetRemainingCount(CardId.DDSavantKepler, 1); + case CardId.LightoftheYangZing: + return Bot.GetRemainingCount(CardId.LightoftheYangZing, 1); + case CardId.Rank_Up_MagicSoulShaveForce: + return Bot.GetRemainingCount(CardId.Rank_Up_MagicSoulShaveForce, 1); + case CardId.SpellPowerMastery: + return Bot.GetRemainingCount(CardId.SpellPowerMastery, 3); + case CardId.DragonShrine: + return Bot.GetRemainingCount(CardId.DragonShrine, 3); + case CardId.Terraforming: + return Bot.GetRemainingCount(CardId.Terraforming, 1); + case CardId.ZefraProvidence: + return Bot.GetRemainingCount(CardId.ZefraProvidence, 3); + case CardId.FoolishBurial: + return Bot.GetRemainingCount(CardId.FoolishBurial, 1); + case CardId.CalledbytheGrave: + return Bot.GetRemainingCount(CardId.CalledbytheGrave, 2); + case CardId.DarkContractwiththGate: + return Bot.GetRemainingCount(CardId.DarkContractwiththGate, 1); + case CardId.OracleofZefra: + return Bot.GetRemainingCount(CardId.OracleofZefra, 3); + case CardId.ZefraWar: + return Bot.GetRemainingCount(CardId.ZefraWar, 1); + case CardId.ZefraDivineStrike: + return Bot.GetRemainingCount(CardId.ZefraDivineStrike, 1); + case CardId.NinePillarsofYangZing: + return Bot.GetRemainingCount(CardId.NinePillarsofYangZing, 1); + default: + return 0; + } + } + } +} diff --git a/Game/AI/Decks/ZoodiacExecutor.cs b/Game/AI/Decks/ZoodiacExecutor.cs index 469eb036..81d3e5f5 100644 --- a/Game/AI/Decks/ZoodiacExecutor.cs +++ b/Game/AI/Decks/ZoodiacExecutor.cs @@ -127,6 +127,7 @@ public override void OnNewTurn() TigermortarSpsummoned = false; ChakanineSpsummoned = false; BroadbullSpsummoned = false; + base.OnNewTurn(); } public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender) @@ -150,6 +151,7 @@ private bool AleisterTheInvokerEffect() { if (Card.Location == CardLocation.Hand) { + if (DefaultCheckWhetherCardIsNegated(Card)) return false; if (!(Duel.Phase == DuelPhase.BattleStep || Duel.Phase == DuelPhase.BattleStart || Duel.Phase == DuelPhase.Damage)) @@ -429,8 +431,7 @@ private bool WhiptailEffect() { if (Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) return false; - if (Card.IsDisabled()) - return false; + if (DefaultCheckWhetherCardIsNegated(Card)) return false; ClientCard target = null; List monsters = Bot.GetMonsters(); foreach (ClientCard monster in monsters) diff --git a/Game/GameBehavior.cs b/Game/GameBehavior.cs index 4f7732aa..c5efe743 100644 --- a/Game/GameBehavior.cs +++ b/Game/GameBehavior.cs @@ -139,6 +139,10 @@ private void RegisterPackets() _messages.Add(GameMessage.AttackDisabled, OnAttackDisabled); _messages.Add(GameMessage.PosChange, OnPosChange); _messages.Add(GameMessage.Chaining, OnChaining); + _messages.Add(GameMessage.ChainSolving, OnChainSolving); + _messages.Add(GameMessage.ChainNegated, OnChainNegated); + _messages.Add(GameMessage.ChainDisabled, OnChainDisabled); + _messages.Add(GameMessage.ChainSolved, OnChainSolved); _messages.Add(GameMessage.ChainEnd, OnChainEnd); _messages.Add(GameMessage.SortCard, OnCardSorting); _messages.Add(GameMessage.SortChain, OnChainSorting); @@ -424,7 +428,14 @@ private void OnStart(BinaryReader packet) deck = packet.ReadInt16(); extra = packet.ReadInt16(); _duel.Fields[GetLocalPlayer(1)].Init(deck, extra); - + //just in case of ending the duel in chain's solving + _duel.CurrentChain.Clear(); + _duel.CurrentChainInfo.Clear(); + _duel.ChainTargets.Clear(); + _duel.ChainTargetOnly.Clear(); + _duel.SummoningCards.Clear(); + _duel.SolvingChainIndex = 0; + _duel.NegatedChainIndexList.Clear(); Logger.DebugWriteLine("Duel started: " + _room.Names[0] + " versus " + _room.Names[1]); _ai.OnStart(); } @@ -814,6 +825,9 @@ private void OnChaining(BinaryReader packet) if (card.Id == 0) card.SetId(cardId); int cc = GetLocalPlayer(packet.ReadByte()); + packet.ReadByte(); // trigger location + packet.ReadInt32(); // trigger sequence + long desc = packet.ReadInt64(); if (_debug) if (card != null) Logger.WriteLine("(" + cc.ToString() + " 's " + (card.Name ?? "UnKnowCard") + " activate effect)"); _ai.OnChaining(card, cc); @@ -821,18 +835,46 @@ private void OnChaining(BinaryReader packet) _duel.ChainTargetOnly.Clear(); _duel.LastSummonPlayer = -1; _duel.CurrentChain.Add(card); + _duel.CurrentChainInfo.Add(new ChainInfo(card, cc, desc)); _duel.LastChainPlayer = cc; } + private void OnChainSolving(BinaryReader packet) + { + int chainIndex = packet.ReadByte(); + _duel.SolvingChainIndex = chainIndex; + } + + private void OnChainNegated(BinaryReader packet) + { + int chainIndex = packet.ReadByte(); + _duel.NegatedChainIndexList.Add(chainIndex); + } + + private void OnChainDisabled(BinaryReader packet) + { + int chainIndex = packet.ReadByte(); + _duel.NegatedChainIndexList.Add(chainIndex); + } + + private void OnChainSolved(BinaryReader packet) + { + int chainIndex = packet.ReadByte(); + _ai.OnChainSolved(chainIndex); + } + private void OnChainEnd(BinaryReader packet) { _duel.MainPhaseEnd = false; _ai.OnChainEnd(); _duel.LastChainPlayer = -1; _duel.CurrentChain.Clear(); + _duel.CurrentChainInfo.Clear(); _duel.ChainTargets.Clear(); _duel.ChainTargetOnly.Clear(); + _duel.SolvingChainIndex = 0; + _duel.NegatedChainIndexList.Clear(); } private void OnCardSorting(BinaryReader packet) diff --git a/README.md b/README.md index d79f2501..0cd77875 100644 --- a/README.md +++ b/README.md @@ -7,25 +7,32 @@ This is a simple, deterministic artificial intelligence that connects as a virtual player to the YGOPro room system. Decks for this bot player **must** be specifically prepared and compiled as individual executors. -Written in C# targeting .NET Framework 4. Use Visual Studio 2015 or newer. +Written in C# 7 targeting .NET Framework 4. Use Visual Studio 2015 or newer. ## Available decks and executors * ABC +* Apophis * Altergeist +* Branded * Blue-Eyes * Blue-Eyes Ritual * Burn * Chain Burn * Cyberse * Dark Magician +* Dogmatika * Dragma * Dragunity * Dragun of Red-Eyes +* Exosister * Frog * Gren Maju Stun * Horus * Kashtira * Lightsworn Shaddoll Dino +* Labrynth +* Maliss +* Maliss OCG * Mathmech * Normal Monster Mash * Normal Monster Mash II @@ -34,8 +41,10 @@ Written in C# targeting .NET Framework 4. Use Visual Studio 2015 or newer. * R5NK * Rainbow * Rose Scrap Synchro +* Ryzeal * Salamangreat * Sky Striker +* Superheavy Samurai * Thunder Dragon * Tearlaments * Time Thief @@ -44,6 +53,8 @@ Written in C# targeting .NET Framework 4. Use Visual Studio 2015 or newer. * Windwitch Gusto * Witchcrafter Grass * Yosenju +* Yubel +* Zefra * ZEXAL Weapon * Zoodiac diff --git a/WindBot.csproj b/WindBot.csproj index 5b50096e..6659c869 100644 --- a/WindBot.csproj +++ b/WindBot.csproj @@ -15,7 +15,7 @@ WindBot WindBot v4.0 - 6 + 7 512 @@ -58,6 +58,17 @@ + + + + + + + + + + + @@ -71,7 +82,6 @@ - diff --git a/bots.json b/bots.json index 145f1bd5..e363bbd2 100644 --- a/bots.json +++ b/bots.json @@ -11,6 +11,12 @@ "difficulty": 3, "masterRules": [ 4, 5 ] }, + { + "name": "Apophis", + "deck": "Apophis", + "difficulty": 3, + "masterRules": [ 4, 5 ] + }, { "name": "Blue-Eyes", "deck": "Blue-Eyes", @@ -29,6 +35,12 @@ "difficulty": 3, "masterRules": [ 4, 5 ] }, + { + "name": "Branded", + "deck": "Albaz", + "difficulty": 3, + "masterRules": [ 3, 5 ] + }, { "name": "Burn", "deck": "Burn", @@ -57,6 +69,12 @@ "name": "Dogmatika", "deck": "Dogmatika", "difficulty": 3, + "masterRules": [ 4, 5 ] + }, + { + "name": "Dragma", + "deck": "Dragma", + "difficulty": 3, "masterRules": [ 5 ] }, { @@ -71,6 +89,12 @@ "difficulty": 2, "masterRules": [ 3, 4, 5 ] }, + { + "name": "Exosister", + "deck": "Exosister", + "difficulty": 3, + "masterRules": [ 5 ] + }, { "name": "Familiar Possessed", "deck": "FamiliarPossessed", @@ -107,12 +131,30 @@ "difficulty": 3, "masterRules": [ 4, 5 ] }, + { + "name": "Labrynth", + "deck": "Labrynth", + "difficulty": 3, + "masterRules": [ 4, 5 ] + }, { "name": "Lightsworn Shaddoll Dino", "deck": "LightswornShaddoldinosour", "difficulty": 3, "masterRules": [ 3, 4, 5 ] }, + { + "name": "Maliss", + "deck": "Maliss", + "difficulty": 3, + "masterRules": [ 5 ] + }, + { + "name": "Maliss OCG", + "deck": "MalissOCG", + "difficulty": 3, + "masterRules": [ 5 ] + }, { "name": "Mathmech", "deck": "MathMech", @@ -167,6 +209,12 @@ "difficulty": 2, "masterRules": [ 3, 4, 5 ] }, + { + "name": "Ryzeal", + "deck": "Ryzeal", + "difficulty": 3, + "masterRules": [ 5 ] + }, { "name": "Salamangreat", "deck": "Salamangreat", @@ -179,6 +227,12 @@ "difficulty": 3, "masterRules": [ 4, 5 ] }, + { + "name": "Superheavy Samurai", + "deck": "SuperheavySamurai", + "difficulty": 3, + "masterRules": [ 4, 5 ] + }, { "name": "Swordsoul", "deck": "Swordsoul", @@ -233,6 +287,18 @@ "difficulty": 0, "masterRules": [ 3, 4, 5 ] }, + { + "name": "Yubel", + "deck": "Yubel", + "difficulty": 3, + "masterRules": [ 5 ] + }, + { + "name": "Zefra", + "deck": "Zefra", + "difficulty": 3, + "masterRules": [ 5 ] + }, { "name": "ZEXAL Weapon", "deck": "Zexal Weapons", diff --git a/libWindbot.csproj b/libWindbot.csproj index 87a79a85..1b6fbdfb 100644 --- a/libWindbot.csproj +++ b/libWindbot.csproj @@ -47,10 +47,21 @@ + + + + + + + + + + + @@ -60,7 +71,6 @@ -