427 lines
17 KiB
C#
427 lines
17 KiB
C#
using Vanguard.Rules.Cards;
|
||
|
||
namespace Vanguard.Rules.Engine.Abilities;
|
||
|
||
/// <summary>Скрипты карт первого сета, подключённые к движку.</summary>
|
||
public static class CardScripts
|
||
{
|
||
public static IReadOnlyDictionary<string, CardScript> Default { get; } =
|
||
new Dictionary<string, CardScript>(StringComparer.Ordinal)
|
||
{
|
||
["barcgal"] = new BarcgalScript(),
|
||
["wingal"] = new WingalScript(),
|
||
["king-of-knights-alfred"] = new AlfredScript(),
|
||
["blaster-blade"] = new BlasterBladeScript(),
|
||
["lake-maiden-lien"] = new DrawOneScript("lake-maiden-lien"),
|
||
["dragon-monk-gojo"] = new DrawOneScript("dragon-monk-gojo"),
|
||
["screamin-and-dancin-announcer-shout"] = new DrawOneScript("screamin-and-dancin-announcer-shout"),
|
||
["lizard-soldier-conroe"] = new ConroeScript(),
|
||
["battle-sister-cocoa"] = new CocoaScript(),
|
||
["tyrant-deathrex"] = new DeathrexScript(),
|
||
["demon-eater"] = new DemonEaterScript(),
|
||
["maiden-of-libra"] = new MaidenOfLibraScript(),
|
||
["knight-of-rose-morgana"] = new MorganaScript(),
|
||
["battle-sister-mocha"] = new BattleSisterMochaScript(),
|
||
["covenant-knight-randolf"] = new MoreHandsPowerScript("covenant-knight-randolf"),
|
||
["stealth-beast-chigasumi"] = new MoreHandsPowerScript("stealth-beast-chigasumi"),
|
||
["queen-of-heart"] = new NamedBoostScript("queen-of-heart", "king-of-sword"),
|
||
["wyvern-strike-jarran"] = new NamedBoostScript("wyvern-strike-jarran", "wyvern-strike-tejas"),
|
||
["nightmare-baby"] = new NamedBoostScript("nightmare-baby", "blue-dust"),
|
||
["weather-girl-milk"] = new WeatherGirlMilkScript(),
|
||
["hungry-dumpty"] = new FlipDamageOnPlaceScript("hungry-dumpty", Clan.NovaGrappler),
|
||
["clay-doll-mechanic"] = new FlipDamageOnPlaceScript("clay-doll-mechanic", Clan.NovaGrappler),
|
||
["blue-dust"] = new BlueDustScript(),
|
||
["karma-queen"] = new KarmaQueenScript(),
|
||
["hell-spider"] = new HellSpiderScript(),
|
||
["dragonic-overlord"] = new DragonicOverlordScript(),
|
||
["solitary-knight-gancelot"] = new GancelotScript(),
|
||
["embodiment-of-victory-aleph"] = new AlephScript(),
|
||
["oracle-guardian-apollon"] = new ApollonScript(),
|
||
["demon-slaying-knight-lohengrin"] = new LohengrinScript(),
|
||
["vortex-dragon"] = new VortexDragonScript(),
|
||
["ceo-amaterasu"] = new CeoAmaterasuScript(),
|
||
["mr-invincible"] = new MrInvincibleScript(),
|
||
["brutal-jack"] = new BrutalJackScript(),
|
||
["asura-kaiser"] = new AsuraKaiserScript(),
|
||
["juggernaut-maximum"] = new ReturnToDeckOnAttackScript("juggernaut-maximum"),
|
||
["highspeed-brakki"] = new ReturnToDeckOnAttackScript("highspeed-brakki"),
|
||
["lozenge-magus"] = new ReturnToDeckOnBoostScript("lozenge-magus", Clan.OracleThinkTank),
|
||
["battleraizer"] = new ReturnToDeckOnBoostScript("battleraizer", Clan.NovaGrappler),
|
||
["rock-the-wall"] = new DropToSoulFromGuardianScript("rock-the-wall"),
|
||
["redshoe-milly"] = new DropToSoulFromGuardianScript("redshoe-milly"),
|
||
["guiding-zombie"] = new GuidingZombieScript(),
|
||
["stealth-dragon-voidmaster"] = new VoidmasterScript(),
|
||
["stealth-beast-hagakure"] = new HagakureScript(),
|
||
["stealth-dragon-dreadmaster"] = new DreadmasterScript(),
|
||
["monster-frank"] = new MonsterFrankScript(),
|
||
["assault-dragon-blightops"] = new BlightopsScript(),
|
||
["future-knight-llew"] = new LlewScript(),
|
||
["dragon-knight-aleph"] = new DragonKnightAlephScript(),
|
||
["crimson-butterfly-brigitte"] = new DriveCheckGradeScript("crimson-butterfly-brigitte", Clan.RoyalPaladin),
|
||
["knight-of-the-harp-tristan"] = new DriveCheckGradeScript("knight-of-the-harp-tristan", Clan.RoyalPaladin),
|
||
["knight-of-conviction-bors"] = new BorsScript(),
|
||
["starlight-unicorn"] = new StarlightUnicornScript(),
|
||
["dragon-monk-goku"] = new GokuScript(),
|
||
["berserk-dragon"] = new BerserkDragonScript(),
|
||
["flame-of-hope-aermo"] = new AermoScript(),
|
||
["demonic-dragon-madonna-joka"] = new OpponentRetireBonusScript("demonic-dragon-madonna-joka"),
|
||
["demonic-dragon-mage-rakshasa"] = new OpponentRetireBonusScript("demonic-dragon-mage-rakshasa"),
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// Knight of Rose, Morgana: при атаке можно сбросить карту из руки и получить +4000 до конца хода.
|
||
/// </summary>
|
||
public sealed class MorganaScript : CardScript
|
||
{
|
||
public override string NameId => "knight-of-rose-morgana";
|
||
|
||
public override IEnumerable<AbilityDefinition> Abilities
|
||
{
|
||
get
|
||
{
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnAttackDeclared,
|
||
Optional = true,
|
||
CanPay = ctx => ctx.State.Players[ctx.Owner].Hand.Count > 0,
|
||
Resolve = ctx => ctx.Engine.AskHandCardChoice(
|
||
ctx.Owner,
|
||
"Сбросьте карту из руки",
|
||
null,
|
||
card =>
|
||
{
|
||
var p = ctx.State.Players[ctx.Owner];
|
||
p.Hand.Remove(card);
|
||
p.Drop.Add(card);
|
||
ctx.Engine.AddPower(
|
||
ctx.Source,
|
||
4000,
|
||
ctx.Source,
|
||
"morgana",
|
||
ModifierDuration.UntilEndOfTurn);
|
||
}),
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>Tyrant, Deathrex: когда эта карта атакует, получает +5000 до конца боя.</summary>
|
||
public sealed class DeathrexScript : CardScript
|
||
{
|
||
public override string NameId => "tyrant-deathrex";
|
||
|
||
public override IEnumerable<AbilityDefinition> Abilities
|
||
{
|
||
get
|
||
{
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnAttackDeclared,
|
||
Resolve = ctx => ctx.Engine.AddBattlePower(ctx.Source, 5000),
|
||
};
|
||
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnAttackHit,
|
||
Condition = ctx => ctx.Engine.IsVanguardOf(ctx.Source, ctx.Owner),
|
||
Resolve = ctx =>
|
||
{
|
||
var slots = ctx.Engine.RearGuardSlots(ctx.Owner, _ => true);
|
||
if (slots.Count == 0)
|
||
return;
|
||
|
||
ctx.Engine.AskUnitChoice(
|
||
ctx.Owner,
|
||
ctx.Owner,
|
||
"Выберите свой Арьергард и уничтожьте его",
|
||
slots,
|
||
slot => ctx.Engine.RetireRearGuard(ctx.Owner, slot));
|
||
},
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>Demon Eater: в начале Основной фазы Soul Charge 1 и +2000 до конца хода.</summary>
|
||
public sealed class DemonEaterScript : CardScript
|
||
{
|
||
public override string NameId => "demon-eater";
|
||
|
||
public override IEnumerable<AbilityDefinition> Abilities
|
||
{
|
||
get
|
||
{
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnPhaseStart,
|
||
Resolve = ctx =>
|
||
{
|
||
ctx.Engine.SoulCharge(ctx.Owner, 1);
|
||
ctx.Engine.AddPower(
|
||
ctx.Source,
|
||
2000,
|
||
ctx.Source,
|
||
"demon-eater",
|
||
ModifierDuration.UntilEndOfTurn);
|
||
},
|
||
};
|
||
|
||
yield return Bt01Abilities.RetireAllOnHit();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>Maiden of Libra: когда атака попадает, Counter Blast 2 и взять карту.</summary>
|
||
public sealed class MaidenOfLibraScript : CardScript
|
||
{
|
||
public override string NameId => "maiden-of-libra";
|
||
|
||
public override IEnumerable<AbilityDefinition> Abilities
|
||
{
|
||
get
|
||
{
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnAttackHit,
|
||
Optional = true,
|
||
CanPay = ctx => ctx.Engine.CanCounterBlast(ctx.Owner, 2),
|
||
Pay = ctx => ctx.Engine.CounterBlast(ctx.Owner, 2),
|
||
Resolve = ctx => ctx.Engine.DrawCards(ctx.Owner, 1),
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Battle Sister, Cocoa: при размещении на VC/RC, если Авангард Oracle Think Tank,
|
||
/// посмотреть верхнюю карту и оставить её на верху или отправить в низ.
|
||
/// </summary>
|
||
public sealed class CocoaScript : CardScript
|
||
{
|
||
public override string NameId => "battle-sister-cocoa";
|
||
|
||
public override IEnumerable<AbilityDefinition> Abilities
|
||
{
|
||
get
|
||
{
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnPlace,
|
||
Condition = ctx => ctx.Engine.VanguardClan(ctx.Owner) == Clan.OracleThinkTank,
|
||
Resolve = ctx => ctx.Engine.LookTopAndReorder(ctx.Owner, "Верхняя карта колоды"),
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Lizard Soldier, Conroe: [ACT](RC): Counter Blast 1 и уничтожить эту карту →
|
||
/// найти в колоде Kagero 1 ранга или ниже и добавить в руку.
|
||
/// </summary>
|
||
public sealed class ConroeScript : CardScript
|
||
{
|
||
public override string NameId => "lizard-soldier-conroe";
|
||
|
||
public override IEnumerable<ActivatedAbility> Activated
|
||
{
|
||
get
|
||
{
|
||
yield return new ActivatedAbility
|
||
{
|
||
CanActivate = ctx => ctx.Engine.CanCounterBlast(ctx.Owner, 1),
|
||
RetiresSelf = true,
|
||
Resolve = ctx =>
|
||
{
|
||
ctx.Engine.CounterBlast(ctx.Owner, 1);
|
||
ctx.Engine.SearchDeckToHand(
|
||
ctx.Owner,
|
||
"Найдите Kagero 1 ранга или ниже",
|
||
d => d.Clan == Clan.Kagero && d.Grade <= 1);
|
||
},
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// «[ACT]: повернуть эту карту и сбросить карту из руки → взять карту».
|
||
/// Общий скрипт для Lake Maiden, Lien, Dragon Monk, Gojo и Shout.
|
||
/// </summary>
|
||
public sealed class DrawOneScript : CardScript
|
||
{
|
||
public DrawOneScript(string nameId) => NameId = nameId;
|
||
|
||
public override string NameId { get; }
|
||
|
||
public override IEnumerable<ActivatedAbility> Activated
|
||
{
|
||
get
|
||
{
|
||
yield return new ActivatedAbility
|
||
{
|
||
CanActivate = _ => true,
|
||
RestsSelf = true,
|
||
RequiresDiscard = true,
|
||
Resolve = ctx => ctx.Engine.DrawCards(ctx.Owner, 1),
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Blaster Blade: при размещении оплатить Counter Blast 2 и уничтожить Арьергард противника.
|
||
/// На Авангарде — любой, на Арьергарде — только 2 ранга или выше и при Авангарде Royal Paladin.
|
||
/// </summary>
|
||
public sealed class BlasterBladeScript : CardScript
|
||
{
|
||
public override string NameId => "blaster-blade";
|
||
|
||
public override IEnumerable<AbilityDefinition> Abilities
|
||
{
|
||
get
|
||
{
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnPlace,
|
||
Optional = true,
|
||
Condition = ctx => ctx.Engine.IsVanguardOf(ctx.Source, ctx.Owner),
|
||
CanPay = ctx => ctx.Engine.CanCounterBlast(ctx.Owner, 2),
|
||
Pay = ctx => ctx.Engine.CounterBlast(ctx.Owner, 2),
|
||
Resolve = ctx =>
|
||
{
|
||
var opponent = 1 - ctx.Owner;
|
||
var slots = ctx.Engine.RearGuardSlots(opponent, _ => true);
|
||
if (slots.Count == 0)
|
||
return;
|
||
|
||
ctx.Engine.AskUnitChoice(
|
||
ctx.Owner,
|
||
opponent,
|
||
"Выберите Арьергард противника",
|
||
slots,
|
||
slot => ctx.Engine.RetireRearGuard(opponent, slot));
|
||
},
|
||
};
|
||
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnPlace,
|
||
Optional = true,
|
||
Condition = ctx =>
|
||
!ctx.Engine.IsVanguardOf(ctx.Source, ctx.Owner) &&
|
||
ctx.Engine.VanguardClan(ctx.Owner) == Clan.RoyalPaladin,
|
||
CanPay = ctx => ctx.Engine.CanCounterBlast(ctx.Owner, 2),
|
||
Pay = ctx => ctx.Engine.CounterBlast(ctx.Owner, 2),
|
||
Resolve = ctx =>
|
||
{
|
||
var opponent = 1 - ctx.Owner;
|
||
var slots = ctx.Engine.RearGuardSlots(opponent, d => d.Grade >= 2);
|
||
if (slots.Count == 0)
|
||
return;
|
||
|
||
ctx.Engine.AskUnitChoice(
|
||
ctx.Owner,
|
||
opponent,
|
||
"Выберите Арьергард противника 2 ранга или выше",
|
||
slots,
|
||
slot => ctx.Engine.RetireRearGuard(opponent, slot));
|
||
},
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Barcgal: когда другой Royal Paladin сменяет эту карту, вы можете призвать её в круг Арьергарда.
|
||
/// </summary>
|
||
public sealed class BarcgalScript : CardScript
|
||
{
|
||
public override string NameId => "barcgal";
|
||
|
||
public override IEnumerable<AbilityDefinition> Abilities
|
||
{
|
||
get
|
||
{
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnRideOver,
|
||
Optional = true,
|
||
Condition = ctx =>
|
||
ctx.Other is not null &&
|
||
ctx.Catalog.CardOf(ctx.Other.PrintingId).Clan == Clan.RoyalPaladin,
|
||
Resolve = ctx => ctx.Engine.CallFromSoul(ctx.Source, ctx.Owner),
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Wingal: когда эта карта поддерживает Blaster Blade, усиленная карта получает +4000 до конца боя.
|
||
/// </summary>
|
||
public sealed class WingalScript : CardScript
|
||
{
|
||
private const string BlasterBlade = "blaster-blade";
|
||
|
||
public override string NameId => "wingal";
|
||
|
||
public override IEnumerable<AbilityDefinition> Abilities
|
||
{
|
||
get
|
||
{
|
||
yield return new AbilityDefinition
|
||
{
|
||
Trigger = AbilityTrigger.OnBoost,
|
||
Condition = ctx =>
|
||
ctx.Other is not null &&
|
||
ctx.Catalog.CardOf(ctx.Other.PrintingId).NameId == BlasterBlade,
|
||
Resolve = ctx => ctx.Engine.AddBattlePower(ctx.Other!, 4000, ctx.Source),
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// King of Knights, Alfred: в свой ход на круге Авангарда получает +2000
|
||
/// за каждый свой Арьергард Royal Paladin.
|
||
/// </summary>
|
||
public sealed class AlfredScript : CardScript
|
||
{
|
||
public override string NameId => "king-of-knights-alfred";
|
||
|
||
public override IEnumerable<ContinuousAbility> Continuous
|
||
{
|
||
get
|
||
{
|
||
yield return new ContinuousAbility
|
||
{
|
||
Applies = ctx =>
|
||
ctx.Engine.IsVanguardOf(ctx.Source, ctx.Owner) &&
|
||
ctx.State.ActivePlayer == ctx.Owner,
|
||
PowerBonus = ctx => 2000 * ctx.Engine.CountRearGuards(ctx.Owner, Clan.RoyalPaladin),
|
||
};
|
||
}
|
||
}
|
||
|
||
public override IEnumerable<ActivatedAbility> Activated
|
||
{
|
||
get
|
||
{
|
||
yield return new ActivatedAbility
|
||
{
|
||
CanActivate = ctx =>
|
||
ctx.Engine.CanCounterBlast(ctx.Owner, 3) &&
|
||
ctx.Engine.HasEmptyRearGuard(ctx.Owner),
|
||
Resolve = ctx =>
|
||
{
|
||
ctx.Engine.CounterBlast(ctx.Owner, 3);
|
||
ctx.Engine.SearchDeckToCall(
|
||
ctx.Owner,
|
||
"Найдите Royal Paladin 2 ранга или ниже",
|
||
d => d.Clan == Clan.RoyalPaladin && d.Grade <= 2);
|
||
},
|
||
};
|
||
}
|
||
}
|
||
}
|