Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions sim/game/cards/dm06/armored_dragon.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,5 @@ func BolmeteusSteelDragon(c *match.Card) {
c.ManaCost = 7
c.ManaRequirement = []string{civ.Fire}

c.Use(fx.Creature, fx.Doublebreaker, fx.When(fx.BreakShield, func(card *match.Card, ctx *match.Context) {

event, _ := ctx.Event.(*match.BreakShieldEvent)
if event.Source != card {
return
}

ctx.InterruptFlow()
for _, shield := range event.Cards {
moved, err := ctx.Match.Opponent(card.Player).MoveCard(shield.ID, match.SHIELDZONE, match.GRAVEYARD, card.ID)
if err == nil {
ctx.Match.ReportActionInChat(ctx.Match.Opponent(card.Player), fmt.Sprintf("%s was moved to %s's graveyard instead of hand by %s", moved.Name, ctx.Match.Opponent(card.Player).Username(), card.Name))
}
}

}))
c.Use(fx.Creature, fx.Doublebreaker, fx.When(fx.BreakShield, fx.ShieldsToGraveyardInsteadOfHand))
}
32 changes: 32 additions & 0 deletions sim/game/cards/dm12/naga.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dm12

import (
"duel-masters/game/civ"
"duel-masters/game/family"
"duel-masters/game/fx"
"duel-masters/game/match"
)

// CruelNagaAvatarOfFate ...
func CruelNagaAvatarOfFate(c *match.Card) {

c.Name = "Cruel Naga, Avatar of Fate"
c.Power = 9000
c.Civs = []string{civ.Water, civ.Darkness}
c.Family = []string{family.Naga}
c.ManaCost = 6
c.ManaRequirement = []string{civ.Water, civ.Darkness}

c.Use(
fx.Creature,
fx.PutIntoManaZoneTapped,
fx.Doublebreaker,
fx.CantBeBlocked,
fx.VortexEvolution(
family.Merfolk, func(x *match.Card) bool { return x.HasFamily(family.Merfolk) },
family.Chimera, func(x *match.Card) bool { return x.HasFamily(family.Chimera) },
),
fx.When(fx.LeftBattlezone, fx.DestroyAllCreatures(match.DestroyedByMiscAbility)),
)

}
62 changes: 62 additions & 0 deletions sim/game/cards/dm12/pegasus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package dm12

import (
"duel-masters/game/civ"
"duel-masters/game/cnd"
"duel-masters/game/family"
"duel-masters/game/fx"
"duel-masters/game/match"
"fmt"
)

// AuraPegasusAvatarOfLife ...
func AuraPegasusAvatarOfLife(c *match.Card) {

c.Name = "Aura Pegasus, Avatar of Life"
c.Power = 12000
c.Civs = []string{civ.Light, civ.Nature}
c.Family = []string{family.Pegasus}
c.ManaCost = 6
c.ManaRequirement = []string{civ.Light, civ.Nature}

c.Use(
fx.Creature,
fx.PutIntoManaZoneTapped,
fx.Triplebreaker,
fx.VortexEvolution(
family.HornedBeast, func(x *match.Card) bool { return x.HasFamily(family.HornedBeast) },
family.AngelCommand, func(x *match.Card) bool { return x.HasFamily(family.AngelCommand) },
),
fx.When(fx.Attacking, auraPegasusAvatarOfLifeRevealTopCard),
fx.When(fx.LeftBattlezone, auraPegasusAvatarOfLifeRevealTopCard),
)

}

// auraPegasusAvatarOfLifeRevealTopCard implements "reveal the top card of
// your deck. If it's a non-evolution creature, put it into the battle zone.
// Otherwise, put it into your hand." cnd.Evolution is rebuilt for every
// creature at every untap step regardless of zone, so it is already correct
// for a card that has never left the deck.
func auraPegasusAvatarOfLifeRevealTopCard(card *match.Card, ctx *match.Context) {

revealed := card.Player.PeekDeck(1)

if len(revealed) < 1 {
return
}

top := revealed[0]

if top.HasCondition(cnd.Creature) && !top.HasCondition(cnd.Evolution) {
fx.ForcePutCreatureIntoBZ(ctx, top, match.DECK, card)
return
}

moved, err := card.Player.MoveCard(top.ID, match.DECK, match.HAND, card.ID)

if err == nil {
ctx.Match.ReportActionInChat(card.Player, fmt.Sprintf("%s put %s into their hand from the top of their deck by %s's effect", card.Player.Username(), moved.Name, card.Name))
}

}
79 changes: 79 additions & 0 deletions sim/game/cards/dm12/phoenix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package dm12

import (
"duel-masters/game/civ"
"duel-masters/game/family"
"duel-masters/game/fx"
"duel-masters/game/match"
"fmt"
)

// DeathPhoenixAvatarOfDoom ...
func DeathPhoenixAvatarOfDoom(c *match.Card) {

c.Name = "Death Phoenix, Avatar of Doom"
c.Power = 9000
c.Civs = []string{civ.Darkness, civ.Fire}
c.Family = []string{family.Phoenix}
c.ManaCost = 4
c.ManaRequirement = []string{civ.Darkness, civ.Fire}

c.Use(
fx.Creature,
fx.PutIntoManaZoneTapped,
fx.Doublebreaker,
fx.VortexEvolution(
family.ZombieDragon, func(x *match.Card) bool { return x.HasFamily(family.ZombieDragon) },
family.FireBird, func(x *match.Card) bool { return x.HasFamily(family.FireBird) },
),
fx.When(fx.BreakShield, fx.ShieldsToGraveyardInsteadOfHand),
fx.When(fx.LeftBattlezone, fx.OpponentDiscardsHand),
)

}

// soulPhoenixAvatarOfUnitySeparateBases implements "When this card would
// leave the battle zone, only the top card leaves the battle zone instead
// (separate the other cards into 2 creatures)."
//
// Registered before VortexEvolution in SoulPhoenixAvatarOfUnity's c.Use, so
// this runs first for the same CardMoved event: by clearing the attachments
// itself, the shared cascade that VortexEvolution runs afterward (which would
// otherwise carry both bases along to wherever this card is headed) finds
// nothing left to move.
func soulPhoenixAvatarOfUnitySeparateBases(card *match.Card, ctx *match.Context) {

for _, base := range card.Attachments() {
moved, err := card.Player.MoveCard(base.ID, match.HIDDENZONE, match.BATTLEZONE, card.ID)

if err == nil && moved.Zone == match.BATTLEZONE {
ctx.Match.ReportActionInChat(card.Player, fmt.Sprintf("%s separated from %s and returned to the battle zone", moved.Name, card.Name))
}
}

card.ClearAttachments()

}

// SoulPhoenixAvatarOfUnity ...
func SoulPhoenixAvatarOfUnity(c *match.Card) {

c.Name = "Soul Phoenix, Avatar of Unity"
c.Power = 13000
c.Civs = []string{civ.Fire, civ.Nature}
c.Family = []string{family.Phoenix}
c.ManaCost = 4
c.ManaRequirement = []string{civ.Fire, civ.Nature}

c.Use(
fx.Creature,
fx.PutIntoManaZoneTapped,
fx.Triplebreaker,
fx.When(fx.LeftBattlezone, soulPhoenixAvatarOfUnitySeparateBases),
fx.VortexEvolution(
family.FireBird, func(x *match.Card) bool { return x.HasFamily(family.FireBird) },
family.EarthDragon, func(x *match.Card) bool { return x.HasFamily(family.EarthDragon) },
),
)

}
32 changes: 32 additions & 0 deletions sim/game/cards/dm12/starnoid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dm12

import (
"duel-masters/game/civ"
"duel-masters/game/family"
"duel-masters/game/fx"
"duel-masters/game/match"
)

// WiseStarnoidAvatarOfHope ...
func WiseStarnoidAvatarOfHope(c *match.Card) {

c.Name = "Wise Starnoid, Avatar of Hope"
c.Power = 9000
c.Civs = []string{civ.Light, civ.Water}
c.Family = []string{family.Starnoid}
c.ManaCost = 5
c.ManaRequirement = []string{civ.Light, civ.Water}

c.Use(
fx.Creature,
fx.PutIntoManaZoneTapped,
fx.Doublebreaker,
fx.VortexEvolution(
family.LightBringer, func(x *match.Card) bool { return x.HasFamily(family.LightBringer) },
family.CyberLord, func(x *match.Card) bool { return x.HasFamily(family.CyberLord) },
),
fx.When(fx.Attacking, fx.TopCardToShield),
fx.When(fx.LeftBattlezone, fx.TopCardToShield),
)

}
5 changes: 5 additions & 0 deletions sim/game/cards/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,11 @@ var DM12 = map[string]match.CardConstructor{
"05c5496d-e5fa-4691-8542-2d6c6919f402": dm12.UlarusPunishmentElemental,
"76310adb-f7c2-4545-8b71-2332b36fbb83": dm12.Gigavrand,
"0fe53ab5-bef9-4bbd-bb05-c94ccb9b1342": dm12.CosmicDarts,
"ba894f7e-b7d7-409e-8393-cab4285a879c": dm12.WiseStarnoidAvatarOfHope,
"23475e43-4cbd-4054-a90d-fde230a28db3": dm12.CruelNagaAvatarOfFate,
"dcc5ee70-bc60-420d-92e5-ed8bd7ff949f": dm12.DeathPhoenixAvatarOfDoom,
"5ffd2e9f-98bc-4e36-8454-8f822740f8eb": dm12.AuraPegasusAvatarOfLife,
"cfa7c730-2818-4f78-aa4b-1395060cd687": dm12.SoulPhoenixAvatarOfUnity,
}

// Promo is a map with all the card id's in the game and corresponding CardConstructor for promotional exclusive cards
Expand Down
1 change: 1 addition & 0 deletions sim/game/cnd/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
ReducedCost = "reduced_cost"
IncreasedCost = "increased_cost"
Evolution = "evolution"
PendingEvolutionTapState = "pending_evolution_tap_state"
Survivor = "survivor"
SpeedAttacker = "speed_attacker"
EvolveIntoAnyFamily = "evolve_into_any_family"
Expand Down
8 changes: 8 additions & 0 deletions sim/game/family/families.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,19 @@ const (
MeltWarrior = "Melt Warrior"
Merfolk = "Merfolk"
MysteryTotem = "Mystery Totem"
Naga = "Naga"
PandorasBox = "Pandora's Box"
ParasiteWorm = "Parasite Worm"
Pegasus = "Pegasus"
Phoenix = "Phoenix"
RainbowPhantom = "Rainbow Phantom"
RockBeast = "Rock Beast"
SeaHacker = "Sea Hacker"
SnowFaerie = "Snow Faerie"
Soltrooper = "Soltrooper"
SpiritQuartz = "Spirit Quartz"
StarlightTree = "Starlight Tree"
Starnoid = "Starnoid"
Survivor = "Survivor"
TreeFolk = "Tree Folk"
VolcanoDragon = "Volcano Dragon"
Expand Down Expand Up @@ -107,15 +111,19 @@ var Families = []string{
MeltWarrior,
Merfolk,
MysteryTotem,
Naga,
PandorasBox,
ParasiteWorm,
Pegasus,
Phoenix,
RainbowPhantom,
RockBeast,
SeaHacker,
SnowFaerie,
Soltrooper,
SpiritQuartz,
StarlightTree,
Starnoid,
Survivor,
TreeFolk,
VolcanoDragon,
Expand Down
59 changes: 49 additions & 10 deletions sim/game/fx/evolution.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func handleEvolutionEvents(card *match.Card, ctx *match.Context, evolvableCreatu
creature := creatures[0]

card.ClearAttachments()
card.Tapped = creature.Tapped
stagePendingEvolutionTapState(card, creature.Tapped)
card.Player.MoveCard(creature.ID, match.BATTLEZONE, match.HIDDENZONE, card.ID)
card.Attach(creature)

Expand All @@ -135,20 +135,59 @@ func handleEvolutionEvents(card *match.Card, ctx *match.Context, evolvableCreatu
}))
}

// Card moved
if event, ok := ctx.Event.(*match.CardMoved); ok {
handleEvolutionCardMoved(card, ctx)

if event.CardID != card.ID || event.To == match.BATTLEZONE || event.To == match.HIDDENZONE {
return
}
}

// stagePendingEvolutionTapState records the tap state an evolution creature
// (regular or vortex) should have once it lands in the battle zone.
//
// It cannot just be assigned to card.Tapped directly: CardPlayedEvent, where
// the base is chosen, fires before the evolution creature itself is moved
// from hand to the battle zone, and every successful Player.MoveCard
// unconditionally resets Tapped to false on arrival - wiping out an
// assignment made here. Staging it as a condition lets
// handleEvolutionCardMoved re-apply it once that reset has already happened.
func stagePendingEvolutionTapState(card *match.Card, tapped bool) {
card.AddUniqueSourceCondition(cnd.PendingEvolutionTapState, tapped, card.ID)
}

// handleEvolutionCardMoved keeps an evolution creature (regular or vortex) in
// sync with its own CardMoved events:
// - arriving in the battle zone applies the tap state stagePendingEvolutionTapState
// recorded during CardPlayedEvent, undoing the Player.MoveCard reset;
// - leaving the battle zone for anywhere but the hidden zone takes every
// attached base along with it, however many there are. Neither regular
// nor vortex evolution cares how many cards are attached, only that the
// whole pile relocates together.
func handleEvolutionCardMoved(card *match.Card, ctx *match.Context) {

event, ok := ctx.Event.(*match.CardMoved)

for _, creature := range card.Attachments() {
card.Player.MoveCard(creature.ID, match.HIDDENZONE, event.To, card.ID)
ctx.Match.ReportActionInChat(card.Player, fmt.Sprintf("%s was sent to the %s together with %s", creature.Name, event.To, card.Name))
if !ok || event.CardID != card.ID {
return
}

if event.To == match.BATTLEZONE {
if condition, err := card.GetCondition(cnd.PendingEvolutionTapState); err == nil {
if tapped, ok := condition.Val.(bool); ok {
card.Tapped = tapped
}
card.RemoveSpecificConditionBySource(cnd.PendingEvolutionTapState, card.ID)
}

card.ClearAttachments()
return
}

if event.To == match.HIDDENZONE {
return
}

for _, creature := range card.Attachments() {
card.Player.MoveCard(creature.ID, match.HIDDENZONE, event.To, card.ID)
ctx.Match.ReportActionInChat(card.Player, fmt.Sprintf("%s was sent to the %s together with %s", creature.Name, event.To, card.Name))
}

card.ClearAttachments()

}
13 changes: 13 additions & 0 deletions sim/game/fx/quality_of_life.go
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,19 @@ func InTheBattlezone(card *match.Card, ctx *match.Context) bool {
return false
}

// LeftBattlezone returns true if the card itself just left the battle zone
// for anywhere other than the hidden zone. Moving under a new evolution is
// not "leaving the battle zone" - the pile is still considered in play.
func LeftBattlezone(card *match.Card, ctx *match.Context) bool {
event, ok := ctx.Event.(*match.CardMoved)

return ok &&
event.CardID == card.ID &&
event.From == match.BATTLEZONE &&
event.To != match.BATTLEZONE &&
event.To != match.HIDDENZONE
}

// SpellCast returns true if the spell was cast
func SpellCast(card *match.Card, ctx *match.Context) bool {

Expand Down
Loading
Loading