Skip to content
Draft
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
1 change: 0 additions & 1 deletion docs/Card-scripting-API/Card-scripting-API.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ CARDNAME is replaced by the card's name ingame.
- CARDNAME must be blocked if able.
- Remove CARDNAME from your deck before playing if you're not playing for ante.
- You may choose not to untap CARDNAME during your untap step.
- CantSearchLibrary

# General SVars
* `SoundEffect:<file.mp3>`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import forge.game.replacement.ReplacementType;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.staticability.StaticAbilitySearchLibrary;
import forge.game.trigger.TriggerType;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
Expand Down Expand Up @@ -1015,9 +1016,9 @@ else if (!origin.contains(ZoneType.Library) && !origin.contains(ZoneType.Hand)
if (origin.contains(ZoneType.Library) && !sa.hasParam("NoLooking")) {
searchedLibrary = true;

if (decider.hasKeyword("LimitSearchLibrary")) { // Aven Mindcensor
Integer fetchNum = StaticAbilitySearchLibrary.limitSearchLibraryConsideringSize(decider);
if (fetchNum != null) {
fetchList.removeAll(player.getCardsIn(ZoneType.Library));
final int fetchNum = Math.min(player.getCardsIn(ZoneType.Library).size(), 4);
if (fetchNum == 0) {
searchedLibrary = false;
} else {
Expand All @@ -1040,9 +1041,9 @@ else if (!origin.contains(ZoneType.Library) && !origin.contains(ZoneType.Hand)
Set<ZoneType> revealZones = Sets.newHashSet();
Iterable<Card> toReveal = null;
if (origin.contains(ZoneType.Library) && searchedLibrary) {
final int fetchNum = Math.min(player.getCardsIn(ZoneType.Library).size(), 4);
Integer fetchNum = StaticAbilitySearchLibrary.limitSearchLibraryConsideringSize(decider);
// Look at whole library before moving onto choosing a card
toReveal = !decider.hasKeyword("LimitSearchLibrary") ? player.getCardsIn(ZoneType.Library) : player.getCardsIn(ZoneType.Library, fetchNum);
toReveal = fetchNum != null ? player.getCardsIn(ZoneType.Library, fetchNum) : player.getCardsIn(ZoneType.Library);
revealZones.add(ZoneType.Library);
}
if (origin.contains(ZoneType.Hand) && player.isOpponentOf(decider)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import forge.game.player.PlayerActionConfirmMode;
import forge.game.player.PlayerCollection;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbilitySearchLibrary;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.Lang;
Expand Down Expand Up @@ -251,9 +252,8 @@ public void resolve(SpellAbility sa) {
}

final Player searched = AbilityUtils.getDefinedPlayers(host, sa.getParam("QuasiLibrarySearch"), sa).get(0);
final int fetchNum = Math.min(searched.getCardsIn(ZoneType.Library).size(), 4);
CardCollectionView shown = !p.hasKeyword("LimitSearchLibrary")
? searched.getCardsIn(ZoneType.Library) : searched.getCardsIn(ZoneType.Library, fetchNum);
final Integer fetchNum = StaticAbilitySearchLibrary.limitSearchLibraryConsideringSize(p);
final CardCollectionView shown = fetchNum != null ? searched.getCardsIn(ZoneType.Library, fetchNum) : searched.getCardsIn(ZoneType.Library);
DelayedReveal delayedReveal = new DelayedReveal(shown, ZoneType.Library, PlayerView.get(searched),
host.getTranslatedName() + " - " +
Localizer.getInstance().getMessage("lblLookingCardIn") + " ");
Expand Down
4 changes: 2 additions & 2 deletions forge-game/src/main/java/forge/game/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -3723,11 +3723,11 @@ public boolean canSearchLibraryWith(SpellAbility sa, Player targetPlayer) {
return true;
}

if (hasKeyword("CantSearchLibrary")) {
if (StaticAbilitySearchLibrary.cantSearchLibrary(this)) {
return false;
}
return targetPlayer == null || !targetPlayer.equals(sa.getActivatingPlayer())
|| !hasKeyword("Spells and abilities you control can't cause you to search your library.");
|| !StaticAbilitySearchLibrary.cantCauseToSearchLibrary(this);
}

public void addAdditionalVote(long timestamp, int value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ public enum StaticAbilityMode {

// StaticAbilityCountersRemain
CountersRemain,

// StaticAbilityCantSearchLibrary
LimitSearchLibrary,
CantSearchLibrary,
CantCauseToSearchLibrary
;

public static StaticAbilityMode smartValueOf(final String value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package forge.game.staticability;

import forge.game.player.Player;
import forge.game.zone.ZoneType;

import java.util.Optional;

import static forge.game.staticability.StaticAbilityMode.CantSearchLibrary;
import static forge.game.staticability.StaticAbilityMode.LimitSearchLibrary;
import static forge.game.staticability.StaticAbilityMode.CantCauseToSearchLibrary;


public class StaticAbilitySearchLibrary {

/**
* @return maximum number of cards which can be fetched from a library considering its size and search limit or null if there is no limit
*/
public static Integer limitSearchLibraryConsideringSize(Player player) {
Integer limit = limitSearchLibrary(player);
if (limit != null) {
return Math.min(player.getCardsIn(ZoneType.Library).size(), limit);
} else {
return null;
}
}

/**
* @return maximum number of cards which can be revealed from a library or null if there is no limit
*/
public static Integer limitSearchLibrary(Player player) {
return findStaticAbilityForValidPlayer(player, LimitSearchLibrary)
.map(stAb -> Integer.valueOf(stAb.getParam("LimitNum")))
.orElse(null);
}

public static boolean cantSearchLibrary(Player player) {
return findStaticAbilityForValidPlayer(player, CantSearchLibrary)
.filter(stAb -> !stAb.getIgnoreEffectPlayers().contains(player))
.isPresent();
}

public static boolean cantCauseToSearchLibrary(Player player) {
return findStaticAbilityForValidPlayer(player, CantCauseToSearchLibrary).isPresent();
}

private static Optional<StaticAbility> findStaticAbilityForValidPlayer(final Player player, final StaticAbilityMode mode) {
return player.getGame()
.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)
.stream()
.flatMap(card -> card.getStaticAbilities().stream())
.filter(stAb -> stAb.checkConditions(mode) && stAb.matchesValidParam("ValidPlayer", player))
.findAny();
}
}
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/a/ashiok_dream_render.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Name:Ashiok, Dream Render
ManaCost:1 UB UB
Types:Legendary Planeswalker Ashiok
Loyalty:5
S:Mode$ Continuous | Affected$ Opponent | AddKeyword$ Spells and abilities you control can't cause you to search your library. | Description$ Spells and abilities your opponents control can't cause their controller to search their library.
S:Mode$ CantCauseToSearchLibrary | ValidPlayer$ Opponent | Description$ Spells and abilities your opponents control can't cause their controller to search their library.
A:AB$ Mill | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | NumCards$ 4 | ValidTgts$ Player | SubAbility$ DBExileGrave | SpellDescription$ Target player mills four cards. Then exile each opponent's graveyard.
SVar:DBExileGrave:DB$ ChangeZoneAll | Origin$ Graveyard | Destination$ Exile | Defined$ Opponent | ChangeType$ Card
Oracle:Spells and abilities your opponents control can't cause their controller to search their library.\n[-1]: Target player mills four cards. Then exile each opponent's graveyard.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/a/aven_mindcensor.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ Types:Creature Bird Wizard
PT:2/1
K:Flash
K:Flying
S:Mode$ Continuous | Affected$ Player.Opponent | AddKeyword$ LimitSearchLibrary | Description$ If an opponent would search a library, that player searches the top four cards of that library instead.
S:Mode$ LimitSearchLibrary | ValidPlayer$ Player.Opponent | LimitNum$ 4 | Description$ If an opponent would search a library, that player searches the top four cards of that library instead.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically this is a replacement effect
not sure yet if the static approach is better for both

Oracle:Flash\nFlying\nIf an opponent would search a library, that player searches the top four cards of that library instead.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/l/leonin_arbiter.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Name:Leonin Arbiter
ManaCost:1 W
Types:Creature Cat Cleric
PT:2/2
S:Mode$ Continuous | Affected$ Player | AddKeyword$ CantSearchLibrary | IgnoreEffectCost$ 2 | Description$ Players can't search libraries. Any player may pay {2} for that player to ignore this effect until end of turn.
S:Mode$ Continuous,CantSearchLibrary | ValidPlayer$ Player | Affected$ Player | IgnoreEffectCost$ 2 | Description$ Players can't search libraries. Any player may pay {2} for that player to ignore this effect until end of turn.
# TODO: The AI won't activate the effect yet, but then again, it won't activate it even if the human is playing with this card, so it doesn't affect specifically the AI playability of this card.
AI:RemoveDeck:Random
Oracle:Players can't search libraries. Any player may pay {2} for that player to ignore this effect until end of turn.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/m/mindlock_orb.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Name:Mindlock Orb
ManaCost:3 U
Types:Artifact
S:Mode$ Continuous | Affected$ Player | AddKeyword$ CantSearchLibrary | Description$ Players can't search libraries.
S:Mode$ CantSearchLibrary | ValidPlayer$ Player | Description$ Players can't search libraries.
Oracle:Players can't search libraries.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/s/shadow_of_doubt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Name:Shadow of Doubt
ManaCost:UB UB
Types:Instant
A:SP$ Effect | StaticAbilities$ STCantSearch | SubAbility$ DBDraw | SpellDescription$ Players can't search libraries this turn. Draw a card.
SVar:STCantSearch:Mode$ Continuous | Affected$ Player | AddKeyword$ CantSearchLibrary | Description$ Players can't search libraries.
SVar:STCantSearch:Mode$ CantSearchLibrary | ValidPlayer$ Player | Description$ Players can't search libraries.
SVar:DBDraw:DB$ Draw
AI:RemoveDeck:All
Oracle:({U/B} can be paid with either {U} or {B}.)\nPlayers can't search libraries this turn.\nDraw a card.
2 changes: 1 addition & 1 deletion forge-gui/res/cardsfolder/s/stranglehold.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Name:Stranglehold
ManaCost:3 R
Types:Enchantment
S:Mode$ Continuous | Affected$ Opponent | AddKeyword$ CantSearchLibrary | Description$ Your opponents can't search libraries.
S:Mode$ CantSearchLibrary | ValidPlayer$ Opponent | Description$ Your opponents can't search libraries.
R:Event$ BeginTurn | ActiveZones$ Battlefield | ValidPlayer$ Opponent | ExtraTurn$ True | Skip$ True | Description$ If an opponent would begin an extra turn, that player skips that turn instead.
SVar:NonStackingEffect:True
AI:RemoveDeck:Random
Expand Down