From 036e6ec1bfbd24c56c28ceb1a06f1814b111f5f5 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Fri, 11 Oct 2024 20:35:45 +0200 Subject: [PATCH] Ghostly Dancers and support --- .../src/main/java/forge/ai/ability/SetStateAi.java | 2 +- .../java/forge/game/ability/AbilityFactory.java | 11 ++++++++++- .../ability/effects/VillainousChoiceEffect.java | 9 --------- forge-game/src/main/java/forge/game/card/Card.java | 14 +++++++------- .../main/java/forge/game/card/CardProperty.java | 12 ++++++++++++ .../forge/game/spellability/AbilityStatic.java | 9 ++------- .../res/cardsfolder/upcoming/ghostly_dancers.txt | 13 +++++++++++++ forge-gui/res/tokenscripts/w_3_1_spirit_flying.txt | 7 +++++++ 8 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/ghostly_dancers.txt create mode 100644 forge-gui/res/tokenscripts/w_3_1_spirit_flying.txt diff --git a/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java b/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java index da8dcfc2286..7585f7e9b61 100644 --- a/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java @@ -155,7 +155,7 @@ public class SetStateAi extends SpellAbilityAi { } // non-permanent facedown can't be turned face up - if (!card.getRules().getType().isPermanent()) { + if (!card.getRules().getType().isPermanent() || !card.canBeTurnedFaceUp()) { return false; } } else { diff --git a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java index 74f19dc7535..12e7bf6a754 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java @@ -285,7 +285,16 @@ public final class AbilityFactory { final String key = "Choices"; if (mapParams.containsKey(key)) { List names = Lists.newArrayList(mapParams.get(key).split(",")); - spellAbility.setAdditionalAbilityList(key, Lists.transform(names, input -> getSubAbility(state, input, sVarHolder))); + spellAbility.setAdditionalAbilityList(key, Lists.transform(names, input -> { + AbilitySub sub = getSubAbility(state, input, sVarHolder); + if (api == ApiType.GenericChoice) { + // support scripters adding restrictions to filter illegal choices + sub.setRestrictions(new SpellAbilityRestriction()); + makeRestrictions(sub); + } + return sub; + } + )); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/VillainousChoiceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/VillainousChoiceEffect.java index 0df712c17ee..e1a2f19bc0f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/VillainousChoiceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/VillainousChoiceEffect.java @@ -21,15 +21,6 @@ public class VillainousChoiceEffect extends SpellAbilityEffect { for (Player p : getDefinedPlayersOrTargeted(sa)) { int choiceAmount = p.getAdditionalVillainousChoices() + 1; - List saToRemove = Lists.newArrayList(); - - for (SpellAbility saChoice : abilities) { - if (saChoice.getRestrictions() != null && !saChoice.getRestrictions().checkOtherRestrictions(sa.getHostCard(), saChoice, sa.getActivatingPlayer())) { - saToRemove.add(saChoice); - } - } - abilities.removeAll(saToRemove); - // For the AI chooseSAForEffect really should take the least good ability. Currently it just takes the first List chosenSAs = Lists.newArrayList(); for(int i = 0; i < choiceAmount; i++) { diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index c02d8ed1bc0..e7ffd5e6ff1 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -805,6 +805,11 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr return setState(CardStateName.FaceDown, false); } + public boolean canBeTurnedFaceUp() { + Map repParams = AbilityKey.mapFromAffected(this); + return !getGame().getReplacementHandler().cantHappenCheck(ReplacementType.TurnFaceUp, repParams); + } + public void forceTurnFaceUp() { turnFaceUp(false, null); } @@ -813,14 +818,10 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr return turnFaceUp(true, cause); } public boolean turnFaceUp(boolean runTriggers, SpellAbility cause) { - if (!isFaceDown()) { + if (!isFaceDown() || !canBeTurnedFaceUp()) { return false; } - // Check replacement effects - Map repParams = AbilityKey.mapFromAffected(this); - if (game.getReplacementHandler().cantHappenCheck(ReplacementType.TurnFaceUp, repParams)) return false; - CardCollectionView cards = hasMergedCard() ? getMergedCards() : new CardCollection(this); boolean retResult = false; long ts = game.getNextTimestamp(); @@ -855,10 +856,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars, ITr triggerHandler.registerActiveTrigger(this, false); } if (runTriggers) { - // Run replacement effects + Map repParams = AbilityKey.mapFromAffected(this); game.getReplacementHandler().run(ReplacementType.TurnFaceUp, repParams); - // Run triggers final Map runParams = AbilityKey.mapFromCard(this); runParams.put(AbilityKey.Cause, cause); diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index ca64d93b010..2570a8a36ee 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -1924,6 +1924,18 @@ public class CardProperty { if (!card.isGoaded()) { return false; } + } else if (property.equals("FullyUnlocked")) { + if (card.getUnlockedRooms().size() < 2) { + return false; + } + } else if (property.startsWith("canReceiveCounters")) { + if (!card.canReceiveCounters(CounterType.getType(property.split(" ")[1]))) { + return false; + } + } else if (property.equals("canBeTurnedFaceUp")) { + if (!card.canBeTurnedFaceUp()) { + return false; + } } else if (property.equals("NoAbilities")) { if (!card.hasNoAbilities()) { return false; diff --git a/forge-game/src/main/java/forge/game/spellability/AbilityStatic.java b/forge-game/src/main/java/forge/game/spellability/AbilityStatic.java index ed43e2e7611..d4b14fd31ce 100644 --- a/forge-game/src/main/java/forge/game/spellability/AbilityStatic.java +++ b/forge-game/src/main/java/forge/game/spellability/AbilityStatic.java @@ -18,12 +18,8 @@ package forge.game.spellability; import forge.card.mana.ManaCost; -import forge.game.ability.AbilityKey; import forge.game.card.Card; import forge.game.cost.Cost; -import forge.game.replacement.ReplacementType; - -import java.util.Map; /** *

@@ -58,9 +54,8 @@ public abstract class AbilityStatic extends Ability implements Cloneable { // Check if ability can't be attempted because of replacement effect // Initial usage is Karlov Watchdog preventing disguise/morph/cloak/manifest turning face up - if (this.isTurnFaceUp()) { - Map repParams = AbilityKey.mapFromAffected(c); - if (c.getGame().getReplacementHandler().cantHappenCheck(ReplacementType.TurnFaceUp, repParams)) return false; + if (this.isTurnFaceUp() && !c.canBeTurnedFaceUp()) { + return false; } return this.getRestrictions().canPlay(c, this); diff --git a/forge-gui/res/cardsfolder/upcoming/ghostly_dancers.txt b/forge-gui/res/cardsfolder/upcoming/ghostly_dancers.txt new file mode 100644 index 00000000000..42a4348fa7c --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/ghostly_dancers.txt @@ -0,0 +1,13 @@ +Name:Ghostly Dancers +ManaCost:3 W W +Types:Creature Spirit +PT:2/5 +K:Flying +T:Mode$ ChangesZone | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigChoice | TriggerDescription$ When CARDNAME enters, return an enchantment card from your graveyard to your hand or unlock a locked door of a Room you control. +SVar:TrigChoice:DB$ GenericChoice | Choices$ DBReturn,DBUnlock +SVar:DBReturn:DB$ ChangeZone | Hidden$ True | Mandatory$ True | ChangeType$ Enchantment.YouOwn | Origin$ Graveyard | Destination$ Hand | IsPresent$ Enchantment.YouOwn | PresentZone$ Graveyard | SpellDescription$ Return an enchantment card from your graveyard to your hand +SVar:DBUnlock:DB$ UnlockDoor | Mode$ Unlock | Choices$ Room.YouCtrl+!FullyUnlocked | IsPresent$ Room.YouCtrl+!FullyUnlocked | SpellDescription$ Unlock a locked door of a Room you control +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Enchantment.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, create a 3/1 white Spirit creature token with flying. +T:Mode$ FullyUnlock | ValidCard$ Card.Room | ValidPlayer$ You | Secondary$ True | Execute$ TrigToken | TriggerZones$ Battlefield | TriggerDescription$ Eerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, create a 3/1 white Spirit creature token with flying. +SVar:TrigToken:DB$ Token | TokenScript$ w_3_1_spirit_flying +Oracle:Flying\nWhen Ghostly Dancers enters, return an enchantment card from your graveyard to your hand or unlock a locked door of a Room you control.\nEerie — Whenever an enchantment you control enters and whenever you fully unlock a Room, create a 3/1 white Spirit creature token with flying. diff --git a/forge-gui/res/tokenscripts/w_3_1_spirit_flying.txt b/forge-gui/res/tokenscripts/w_3_1_spirit_flying.txt new file mode 100644 index 00000000000..7421d4e5011 --- /dev/null +++ b/forge-gui/res/tokenscripts/w_3_1_spirit_flying.txt @@ -0,0 +1,7 @@ +Name:Spirit Token +ManaCost:no cost +Types:Creature Spirit +Colors:white +PT:3/1 +K:Flying +Oracle:Flying