mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-12 00:38:44 +00:00
Merge branch '1842-panharmonicon-trigger-lingering' into 'master'
Resolve "Panharmonicon trigger lingering" Closes #1842 See merge request core-developers/forge!4708
This commit is contained in:
@@ -1880,7 +1880,7 @@ public class ComputerUtilMana {
|
||||
final Card offering = sa.getSacrificedAsOffering();
|
||||
offering.setUsedToPay(false);
|
||||
if (costIsPaid && !test) {
|
||||
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, null);
|
||||
sa.getHostCard().getGame().getAction().sacrifice(offering, sa, null, null);
|
||||
}
|
||||
sa.resetSacrificedAsOffering();
|
||||
}
|
||||
@@ -1888,7 +1888,7 @@ public class ComputerUtilMana {
|
||||
final Card emerge = sa.getSacrificedAsEmerge();
|
||||
emerge.setUsedToPay(false);
|
||||
if (costIsPaid && !test) {
|
||||
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, null);
|
||||
sa.getHostCard().getGame().getAction().sacrifice(emerge, sa, null, null);
|
||||
}
|
||||
sa.resetSacrificedAsEmerge();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import forge.game.zone.ZoneType;
|
||||
public class DestroyAi extends SpellAbilityAi {
|
||||
@Override
|
||||
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
|
||||
return canPlayAI(ai, sa);
|
||||
return checkApiLogic(ai, sa);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -488,7 +488,32 @@ public class PumpAi extends PumpAiBase {
|
||||
// each player sacrifices one permanent, e.g. Vaevictis, Asmadi the Dire - grab the worst for allied and
|
||||
// the best for opponents
|
||||
return SacrificeAi.doSacOneEachLogic(ai, sa);
|
||||
} else if (sa.getParam("AILogic").equals("Destroy")) {
|
||||
List<Card> tgts = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
|
||||
if (tgts.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Card> alliedTgts = CardLists.filter(tgts, Predicates.or(CardPredicates.isControlledByAnyOf(ai.getAllies()), CardPredicates.isController(ai)));
|
||||
List<Card> oppTgts = CardLists.filter(tgts, CardPredicates.isControlledByAnyOf(ai.getRegisteredOpponents()));
|
||||
|
||||
Card destroyTgt = null;
|
||||
if (!oppTgts.isEmpty()) {
|
||||
destroyTgt = ComputerUtilCard.getBestAI(oppTgts);
|
||||
} else {
|
||||
// TODO: somehow limit this so that the AI doesn't always destroy own stuff when able?
|
||||
destroyTgt = ComputerUtilCard.getWorstAI(alliedTgts);
|
||||
}
|
||||
|
||||
if (destroyTgt != null) {
|
||||
sa.resetTargets();
|
||||
sa.getTargets().add(destroyTgt);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isFight) {
|
||||
return FightAi.canFightAi(ai, sa, attack, defense);
|
||||
}
|
||||
|
||||
@@ -197,6 +197,15 @@ public class Game {
|
||||
}
|
||||
}
|
||||
|
||||
public CardCollectionView copyLastStateBattlefield() {
|
||||
CardCollection result = new CardCollection();
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
for (final Player p : getPlayers()) {
|
||||
result.addAll(p.getZone(ZoneType.Battlefield).getLKICopy(cachedMap));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void updateLastStateForCard(Card c) {
|
||||
if (c == null || c.getZone() == null) {
|
||||
return;
|
||||
|
||||
@@ -1278,7 +1278,7 @@ public class GameAction {
|
||||
orderedNoRegCreats = true;
|
||||
}
|
||||
for (Card c : noRegCreats) {
|
||||
sacrificeDestroy(c, null, table);
|
||||
sacrificeDestroy(c, null, table, null);
|
||||
}
|
||||
}
|
||||
if (desCreats != null) {
|
||||
@@ -1290,7 +1290,7 @@ public class GameAction {
|
||||
orderedDesCreats = true;
|
||||
}
|
||||
for (Card c : desCreats) {
|
||||
destroy(c, null, true, table);
|
||||
destroy(c, null, true, table, null);
|
||||
}
|
||||
}
|
||||
setHoldCheckingStaticAbilities(false);
|
||||
@@ -1376,7 +1376,7 @@ public class GameAction {
|
||||
return false;
|
||||
}
|
||||
if (!game.getStack().hasSourceOnStack(c, SpellAbilityPredicates.isChapter())) {
|
||||
sacrifice(c, null, table);
|
||||
sacrifice(c, null, table, null);
|
||||
checkAgain = true;
|
||||
}
|
||||
return checkAgain;
|
||||
@@ -1404,7 +1404,7 @@ public class GameAction {
|
||||
|
||||
// cleanup aura
|
||||
if (c.isAura() && c.isInPlay() && !c.isEnchanting()) {
|
||||
sacrificeDestroy(c, null, table);
|
||||
sacrificeDestroy(c, null, table, null);
|
||||
checkAgain = true;
|
||||
}
|
||||
return checkAgain;
|
||||
@@ -1556,7 +1556,7 @@ public class GameAction {
|
||||
|
||||
for (Card c : list) {
|
||||
if (c.getCounters(CounterEnumType.LOYALTY) <= 0) {
|
||||
sacrificeDestroy(c, null, table);
|
||||
sacrificeDestroy(c, null, table, null);
|
||||
// Play the Destroy sound
|
||||
game.fireEvent(new GameEventCardDestroyed());
|
||||
recheck = true;
|
||||
@@ -1619,7 +1619,7 @@ public class GameAction {
|
||||
"You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)", null);
|
||||
for (Card c: cc) {
|
||||
if (c != toKeep) {
|
||||
sacrificeDestroy(c, null, table);
|
||||
sacrificeDestroy(c, null, table, null);
|
||||
}
|
||||
}
|
||||
game.fireEvent(new GameEventCardDestroyed());
|
||||
@@ -1653,28 +1653,24 @@ public class GameAction {
|
||||
}
|
||||
|
||||
for (Card c : worlds) {
|
||||
sacrificeDestroy(c, null, table);
|
||||
sacrificeDestroy(c, null, table, null);
|
||||
game.fireEvent(new GameEventCardDestroyed());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public final Card sacrifice(final Card c, final SpellAbility source) {
|
||||
return sacrifice(c, source, null);
|
||||
}
|
||||
public final Card sacrifice(final Card c, final SpellAbility source, CardZoneTable table) {
|
||||
public final Card sacrifice(final Card c, final SpellAbility source, CardZoneTable table, Map<AbilityKey, Object> params) {
|
||||
if (!c.canBeSacrificedBy(source)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
c.getController().addSacrificedThisTurn(c, source);
|
||||
|
||||
return sacrificeDestroy(c, source, table);
|
||||
return sacrificeDestroy(c, source, table, params);
|
||||
}
|
||||
|
||||
public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, CardZoneTable table) {
|
||||
public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, CardZoneTable table, Map<AbilityKey, Object> params) {
|
||||
Player activator = null;
|
||||
if (!c.canBeDestroyed()) {
|
||||
return false;
|
||||
@@ -1685,6 +1681,9 @@ public class GameAction {
|
||||
repRunParams.put(AbilityKey.Source, sa);
|
||||
repRunParams.put(AbilityKey.Affected, c);
|
||||
repRunParams.put(AbilityKey.Regeneration, regenerate);
|
||||
if (params != null) {
|
||||
repRunParams.putAll(params);
|
||||
}
|
||||
|
||||
if (game.getReplacementHandler().run(ReplacementType.Destroy, repRunParams) != ReplacementResult.NotReplaced) {
|
||||
return false;
|
||||
@@ -1701,11 +1700,14 @@ public class GameAction {
|
||||
// Run triggers
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
|
||||
runParams.put(AbilityKey.Causer, activator);
|
||||
if (params != null) {
|
||||
runParams.putAll(params);
|
||||
}
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Destroyed, runParams, false);
|
||||
// in case the destroyed card has such a trigger
|
||||
game.getTriggerHandler().registerActiveLTBTrigger(c);
|
||||
|
||||
final Card sacrificed = sacrificeDestroy(c, sa, table);
|
||||
final Card sacrificed = sacrificeDestroy(c, sa, table, params);
|
||||
return sacrificed != null;
|
||||
}
|
||||
|
||||
@@ -1713,12 +1715,12 @@ public class GameAction {
|
||||
* @return the sacrificed Card in its new location, or {@code null} if the
|
||||
* sacrifice wasn't successful.
|
||||
*/
|
||||
protected final Card sacrificeDestroy(final Card c, SpellAbility cause, CardZoneTable table) {
|
||||
protected final Card sacrificeDestroy(final Card c, SpellAbility cause, CardZoneTable table, Map<AbilityKey, Object> params) {
|
||||
if (!c.isInPlay()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Card newCard = moveToGraveyard(c, cause, null);
|
||||
final Card newCard = moveToGraveyard(c, cause, params);
|
||||
if (table != null && newCard != null && newCard.getZone() != null) {
|
||||
table.put(ZoneType.Battlefield, newCard.getZone().getZoneType(), newCard);
|
||||
}
|
||||
|
||||
@@ -638,6 +638,9 @@ public final class GameActionUtil {
|
||||
}
|
||||
|
||||
public static CardCollectionView orderCardsByTheirOwners(Game game, CardCollectionView list, ZoneType dest, SpellAbility sa) {
|
||||
if (list.size() <= 1) {
|
||||
return list;
|
||||
}
|
||||
CardCollection completeList = new CardCollection();
|
||||
for (Player p : game.getPlayers()) {
|
||||
CardCollection subList = new CardCollection();
|
||||
|
||||
@@ -73,6 +73,7 @@ public enum AbilityKey {
|
||||
IsCombatDamage("IsCombatDamage"),
|
||||
IndividualCostPaymentInstance("IndividualCostPaymentInstance"),
|
||||
IsMadness("IsMadness"),
|
||||
LastStateBattlefield("LastStateBattlefield"),
|
||||
LifeAmount("LifeAmount"), //TODO confirm that this and LifeGained can be merged
|
||||
LifeGained("LifeGained"),
|
||||
Mana("Mana"),
|
||||
|
||||
@@ -173,15 +173,13 @@ public class AbilityUtils {
|
||||
}
|
||||
}
|
||||
else if (defined.equals("Targeted") && sa instanceof SpellAbility) {
|
||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingCard();
|
||||
if (saTargeting != null) {
|
||||
Iterables.addAll(cards, saTargeting.getTargets().getTargetCards());
|
||||
for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) {
|
||||
Iterables.addAll(cards, tc.getTargetCards());
|
||||
}
|
||||
}
|
||||
else if (defined.equals("TargetedSource") && sa instanceof SpellAbility) {
|
||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingSA();
|
||||
if (saTargeting != null) {
|
||||
for (SpellAbility s : saTargeting.getTargets().getTargetSpells()) {
|
||||
for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) {
|
||||
for (SpellAbility s : tc.getTargetSpells()) {
|
||||
cards.add(s.getHostCard());
|
||||
}
|
||||
}
|
||||
@@ -1008,9 +1006,8 @@ public class AbilityUtils {
|
||||
players.addAll(getDefinedPlayers(card, "TargetedController", sa));
|
||||
}
|
||||
else if ((defined.equals("Targeted") || defined.equals("TargetedPlayer")) && sa instanceof SpellAbility) {
|
||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingPlayer();
|
||||
if (saTargeting != null) {
|
||||
players.addAll(saTargeting.getTargets().getTargetPlayers());
|
||||
for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) {
|
||||
players.addAll(tc.getTargetPlayers());
|
||||
}
|
||||
}
|
||||
else if (defined.equals("ParentTarget") && sa instanceof SpellAbility) {
|
||||
@@ -1298,9 +1295,8 @@ public class AbilityUtils {
|
||||
s = ((SpellAbility)sa).getRootAbility();
|
||||
}
|
||||
else if (defined.equals("Targeted") && sa instanceof SpellAbility) {
|
||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingSA();
|
||||
if (saTargeting != null) {
|
||||
for (SpellAbility targetSpell : saTargeting.getTargets().getTargetSpells()) {
|
||||
for (TargetChoices tc : ((SpellAbility)sa).getAllTargetChoices()) {
|
||||
for (SpellAbility targetSpell : tc.getTargetSpells()) {
|
||||
SpellAbilityStackInstance stackInstance = game.getStack().getInstanceFromSpellAbility(targetSpell);
|
||||
if (stackInstance != null) {
|
||||
SpellAbility instanceSA = stackInstance.getSpellAbility(true);
|
||||
@@ -1412,7 +1408,6 @@ public class AbilityUtils {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityUtils.resolveApiAbility(sa, game);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Map;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
@@ -47,6 +48,7 @@ public class BalanceEffect extends SpellAbilityEffect {
|
||||
min = Math.min(min, validCards.get(i).size());
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
Player p = players.get(i);
|
||||
@@ -59,7 +61,7 @@ public class BalanceEffect extends SpellAbilityEffect {
|
||||
} else { // Battlefield
|
||||
for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
|
||||
if ( null == card ) continue;
|
||||
game.getAction().sacrifice(card, sa, table);
|
||||
game.getAction().sacrifice(card, sa, table, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.replacement.ReplacementResult;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -113,6 +114,8 @@ public class CounterEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
for (final SpellAbility tgtSA : sas) {
|
||||
final Card tgtSACard = tgtSA.getHostCard();
|
||||
// should remember even that spell cannot be countered, e.g. Dovescape
|
||||
@@ -137,7 +140,7 @@ public class CounterEffect extends SpellAbilityEffect {
|
||||
|
||||
// Destroy Permanent may be able to be turned into a SubAbility
|
||||
if (tgtSA.isAbility() && sa.hasParam("DestroyPermanent")) {
|
||||
game.getAction().destroy(tgtSACard, sa, true, null);
|
||||
game.getAction().destroy(tgtSACard, sa, true, table, params);
|
||||
}
|
||||
|
||||
if (sa.hasParam("RememberCountered")) {
|
||||
@@ -152,6 +155,7 @@ public class CounterEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
}
|
||||
table.triggerChangesZoneAll(game, sa);
|
||||
} // end counterResolve
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -90,10 +91,12 @@ public class DestroyAllEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
for (Card c : list) {
|
||||
if (game.getAction().destroy(c, sa, !noRegen, table) && remDestroyed) {
|
||||
if (game.getAction().destroy(c, sa, !noRegen, table, params) && remDestroyed) {
|
||||
card.addRemembered(CardUtil.getLKICopy(c, cachedMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -72,13 +73,16 @@ public class DestroyEffect extends SpellAbilityEffect {
|
||||
card.clearRemembered();
|
||||
}
|
||||
|
||||
CardCollection tgtCards = getTargetCards(sa);
|
||||
CardCollection untargetedCards = CardUtil.getRadiance(sa);
|
||||
CardCollectionView tgtCards = getTargetCards(sa);
|
||||
CardCollectionView untargetedCards = CardUtil.getRadiance(sa);
|
||||
|
||||
if (tgtCards.size() > 1) {
|
||||
tgtCards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, tgtCards, ZoneType.Graveyard, sa);
|
||||
tgtCards = GameActionUtil.orderCardsByTheirOwners(game, tgtCards, ZoneType.Graveyard, sa);
|
||||
}
|
||||
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
for (final Card tgtC : tgtCards) {
|
||||
@@ -90,24 +94,24 @@ public class DestroyEffect extends SpellAbilityEffect {
|
||||
if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard)) {
|
||||
continue;
|
||||
}
|
||||
internalDestroy(gameCard, sa, table, cachedMap);
|
||||
internalDestroy(gameCard, sa, table, cachedMap, params);
|
||||
}
|
||||
}
|
||||
|
||||
if (untargetedCards.size() > 1) {
|
||||
untargetedCards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, untargetedCards, ZoneType.Graveyard, sa);
|
||||
untargetedCards = GameActionUtil.orderCardsByTheirOwners(game, untargetedCards, ZoneType.Graveyard, sa);
|
||||
}
|
||||
|
||||
for (final Card unTgtC : untargetedCards) {
|
||||
if (unTgtC.isInPlay()) {
|
||||
internalDestroy(unTgtC, sa, table, cachedMap);
|
||||
internalDestroy(unTgtC, sa, table, cachedMap, params);
|
||||
}
|
||||
}
|
||||
|
||||
table.triggerChangesZoneAll(game, sa);
|
||||
}
|
||||
|
||||
protected void internalDestroy(Card gameCard, SpellAbility sa, CardZoneTable table, Map<Integer, Card> cachedMap) {
|
||||
protected void internalDestroy(Card gameCard, SpellAbility sa, CardZoneTable table, Map<Integer, Card> cachedMap, Map<AbilityKey, Object> params) {
|
||||
final Card card = sa.getHostCard();
|
||||
final Game game = card.getGame();
|
||||
|
||||
@@ -122,9 +126,9 @@ public class DestroyEffect extends SpellAbilityEffect {
|
||||
card.addRemembered(gameCard.getAttachedCards());
|
||||
}
|
||||
if (sac) {
|
||||
destroyed = game.getAction().sacrifice(gameCard, sa, table) != null;
|
||||
destroyed = game.getAction().sacrifice(gameCard, sa, table, params) != null;
|
||||
} else {
|
||||
destroyed = game.getAction().destroy(gameCard, sa, !noRegen, table);
|
||||
destroyed = game.getAction().destroy(gameCard, sa, !noRegen, table, params);
|
||||
}
|
||||
if (destroyed && remDestroyed) {
|
||||
card.addRemembered(gameCard);
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
@@ -86,9 +87,12 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
|
||||
for (Card sac : list) {
|
||||
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
|
||||
if (game.getAction().sacrifice(sac, sa, table) != null) {
|
||||
if (game.getAction().sacrifice(sac, sa, table, params) != null) {
|
||||
if (remSacrificed) {
|
||||
card.addRemembered(lKICopy);
|
||||
}
|
||||
|
||||
@@ -106,10 +106,12 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
final String remSVar = sa.getParam("RememberSacrificedSVar");
|
||||
int countSacrificed = 0;
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
Map<AbilityKey, Object> params = AbilityKey.newMap();
|
||||
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
|
||||
|
||||
if (valid.equals("Self") && game.getZoneOf(card) != null) {
|
||||
if (game.getZoneOf(card).is(ZoneType.Battlefield)) {
|
||||
if (game.getAction().sacrifice(card, sa, table) != null) {
|
||||
if (game.getAction().sacrifice(card, sa, table, params) != null) {
|
||||
countSacrificed++;
|
||||
if (remSacrificed) {
|
||||
card.addRemembered(card);
|
||||
@@ -152,8 +154,8 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
||||
Map<Integer, Card> cachedMap = Maps.newHashMap();
|
||||
for (Card sac : choosenToSacrifice) {
|
||||
final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap);
|
||||
boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, table) != null;
|
||||
boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, table);
|
||||
boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, table, params) != null;
|
||||
boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, table, params);
|
||||
// Run Devour Trigger
|
||||
if (devour) {
|
||||
card.addDevoured(lKICopy);
|
||||
|
||||
@@ -519,28 +519,6 @@ public class CardFactoryUtil {
|
||||
return types.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getNeededXDamage.
|
||||
* </p>
|
||||
*
|
||||
* @param ability
|
||||
* a {@link forge.game.spellability.SpellAbility} object.
|
||||
* @return a int.
|
||||
*/
|
||||
public static int getNeededXDamage(final SpellAbility ability) {
|
||||
// when targeting a creature, make sure the AI won't overkill on X
|
||||
// damage
|
||||
final Card target = ability.getTargetCard();
|
||||
int neededDamage = -1;
|
||||
|
||||
if ((target != null)) {
|
||||
neededDamage = target.getNetToughness() - target.getDamage();
|
||||
}
|
||||
|
||||
return neededDamage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the ability factory abilities.
|
||||
*
|
||||
|
||||
@@ -126,7 +126,7 @@ public class CostSacrifice extends CostPartWithList {
|
||||
@Override
|
||||
protected Card doPayment(SpellAbility ability, Card targetCard) {
|
||||
// no table there, it is already handled by CostPartWithList
|
||||
return targetCard.getGame().getAction().sacrifice(targetCard, ability, null);
|
||||
return targetCard.getGame().getAction().sacrifice(targetCard, ability, null, null);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardZoneTable;
|
||||
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -22,8 +23,17 @@ public class StaticAbilityPanharmonicon {
|
||||
public static int handlePanharmonicon(final Game game, final Trigger t, final Map<AbilityKey, Object> runParams) {
|
||||
int n = 0;
|
||||
|
||||
CardCollectionView cardList = null;
|
||||
// currently only used for leave the battlefield trigger
|
||||
if (runParams.containsKey(AbilityKey.LastStateBattlefield)) {
|
||||
cardList = (CardCollectionView) runParams.get(AbilityKey.LastStateBattlefield);
|
||||
}
|
||||
if (cardList == null) {
|
||||
cardList = t.getMode() == TriggerType.ChangesZone && "Battlefield".equals(t.getParam("Origin")) ? game.getLastStateBattlefield() : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES);
|
||||
}
|
||||
|
||||
// Checks only the battlefield, as those effects only work from there
|
||||
for (final Card ca : t.getMode() == TriggerType.ChangesZone ? game.getLastStateBattlefield() : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final Card ca : cardList) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
if (!stAb.getParam("Mode").equals(MODE) || stAb.isSuppressed() || !stAb.checkConditions()) {
|
||||
continue;
|
||||
|
||||
@@ -2,9 +2,9 @@ Name:Boom
|
||||
ManaCost:1 R
|
||||
AlternateMode: Split
|
||||
Types:Sorcery
|
||||
A:SP$ Destroy | Cost$ 1 R | TgtPrompt$ Choose target land you control to destroy | ValidTgts$ Land.YouCtrl | SubAbility$ DestroyOpp | SpellDescription$ Destroy target land you control and target land you don't control.
|
||||
SVar:DestroyOpp:DB$ Destroy | TgtPrompt$ Choose target land you don't control to destroy | ValidTgts$ Land.YouDontCtrl
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/boombust.jpg
|
||||
A:SP$ Pump | Cost$ 1 R | TgtPrompt$ Choose target land you control to destroy | ValidTgts$ Land.YouCtrl | AILogic$ Destroy | Curse$ True | SubAbility$ DestroyOpp | SpellDescription$ Destroy target land you control and target land you don't control.
|
||||
SVar:DestroyOpp:DB$ Pump | TgtPrompt$ Choose target land you don't control to destroy | ValidTgts$ Land.YouDontCtrl | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
Oracle:Destroy target land you control and target land you don't control.
|
||||
|
||||
ALTERNATE
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Name:Decimate
|
||||
ManaCost:2 R G
|
||||
Types:Sorcery
|
||||
A:SP$ Destroy | Cost$ 2 R G | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SubAbility$ DestroyCreature | SpellDescription$ Destroy target artifact, target creature, target enchantment, and target land.
|
||||
SVar:DestroyCreature:DB$ Destroy | ValidTgts$ Creature | SubAbility$ DestroyEnch | TgtPrompt$ Select target creature
|
||||
SVar:DestroyEnch:DB$ Destroy | ValidTgts$ Enchantment | SubAbility$ DestroyLand | TgtPrompt$ Select target enchantment
|
||||
SVar:DestroyLand:DB$ Destroy | ValidTgts$ Land | TgtPrompt$ Select target land
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/decimate.jpg
|
||||
A:SP$ Pump | Cost$ 2 R G | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SubAbility$ DestroyCreature | AILogic$ Destroy | Curse$ True | SpellDescription$ Destroy target artifact, target creature, target enchantment, and target land.
|
||||
SVar:DestroyCreature:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ Destroy | Curse$ True | SubAbility$ DestroyEnch
|
||||
SVar:DestroyEnch:DB$ Pump | ValidTgts$ Enchantment | TgtPrompt$ Select target enchantment | AILogic$ Destroy | Curse$ True | SubAbility$ DestroyLand
|
||||
SVar:DestroyLand:DB$ Pump | ValidTgts$ Land | TgtPrompt$ Select target land | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
Oracle:Destroy target artifact, target creature, target enchantment, and target land.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Fumarole
|
||||
ManaCost:3 B R
|
||||
Types:Sorcery
|
||||
A:SP$ Destroy | Cost$ 3 B R PayLife<3> | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBDestroy | SpellDescription$ Destroy target creature and target land.
|
||||
SVar:DBDestroy:DB$ Destroy | ValidTgts$ Land | TgtPrompt$ Select target land
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/fumarole.jpg
|
||||
A:SP$ Pump | Cost$ 3 B R PayLife<3> | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ Destroy | Curse$ True | SubAbility$ DBLand | SpellDescription$ Destroy target creature and target land.
|
||||
SVar:DBLand:DB$ Destroy | ValidTgts$ Land | TgtPrompt$ Select target land | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
Oracle:As an additional cost to cast this spell, pay 3 life.\nDestroy target creature and target land.
|
||||
|
||||
@@ -3,6 +3,7 @@ ManaCost:3 R
|
||||
Types:Creature Goblin
|
||||
PT:2/2
|
||||
T:Mode$ AttackerUnblocked | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigDestroyCreature | TriggerDescription$ Whenever CARDNAME attacks and isn't blocked, you may sacrifice it. If you do, destroy target creature and target land.
|
||||
SVar:TrigDestroyCreature:AB$ Destroy | Cost$ Sac<1/CARDNAME> | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBDestroyLand
|
||||
SVar:DBDestroyLand:DB$ Destroy | ValidTgts$ Land | TgtPrompt$ Select target land
|
||||
SVar:TrigDestroyCreature:AB$ Pump | Cost$ Sac<1/CARDNAME> | ValidTgts$ Creature | TgtPrompt$ Select target creature | AILogic$ Destroy | Curse$ True | SubAbility$ DBLand
|
||||
SVar:DBLand:DB$ Pump | ValidTgts$ Land | TgtPrompt$ Select target land | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
Oracle:Whenever Goblin Grenadiers attacks and isn't blocked, you may sacrifice it. If you do, destroy target creature and target land.
|
||||
|
||||
@@ -4,8 +4,8 @@ Types:Sorcery
|
||||
A:SP$ Charm | Cost$ R G | Choices$ DBDestroy1,DBDestroy2,DBDestroy3 | CharmNum$ 1
|
||||
SVar:DBDestroy1:DB$ Destroy | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SpellDescription$ Destroy target artifact.
|
||||
SVar:DBDestroy2:DB$ Destroy | ValidTgts$ Enchantment | TgtPrompt$ Select target enchantment | SpellDescription$ Destroy target enchantment.
|
||||
SVar:DBDestroy3:DB$ Destroy | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SpellDescription$ Destroy target artifact and target enchantment. | SubAbility$ DestroyEnch
|
||||
SVar:DestroyEnch:DB$ Destroy | ValidTgts$ Enchantment | TgtPrompt$ Select target enchantment
|
||||
SVar:DBDestroy3:DB$ Pump | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | AILogic$ Destroy | Curse$ True | SubAbility$ DestroyEnch | SpellDescription$ Destroy target artifact and target enchantment.
|
||||
SVar:DestroyEnch:DB$ Pump | ValidTgts$ Enchantment | TgtPrompt$ Select target enchantment | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/hull_breach.jpg
|
||||
Oracle:Choose one —\n• Destroy target artifact.\n• Destroy target enchantment.\n• Destroy target artifact and target enchantment.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Plague Spores
|
||||
ManaCost:4 B R
|
||||
Types:Sorcery
|
||||
A:SP$ Destroy | Cost$ 4 B R | ValidTgts$ Creature.nonBlack | TgtPrompt$ Select nonblack creature | NoRegen$ True | SubAbility$ DestroyLand | SpellDescription$ Destroy target nonblack creature and target land. They can't be regenerated.
|
||||
SVar:DestroyLand:DB$ Destroy | ValidTgts$ Land | NoRegen$ True | TgtPrompt$ Select target land
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/plague_spores.jpg
|
||||
A:SP$ Pump | Cost$ 4 B R | ValidTgts$ Creature.nonBlack | TgtPrompt$ Select nonblack creature | AILogic$ Destroy | Curse$ True | SubAbility$ DBLand | SpellDescription$ Destroy target nonblack creature and target land. They can't be regenerated.
|
||||
SVar:DBLand:DB$ Pump | ValidTgts$ Land | TgtPrompt$ Select target land | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted | NoRegen$ True
|
||||
Oracle:Destroy target nonblack creature and target land. They can't be regenerated.
|
||||
|
||||
@@ -2,10 +2,10 @@ Name:Reign of Chaos
|
||||
ManaCost:2 R R
|
||||
Types:Sorcery
|
||||
A:SP$ Charm | Cost$ 2 R R | Choices$ DBDestroy1,DBDestroy2 | CharmNum$ 1
|
||||
SVar:DBDestroy1:DB$ Destroy | ValidTgts$ Plains | TgtPrompt$ Select target Plains | SubAbility$ DBDestroyWhite | SpellDescription$ Destroy target Plains and target white creature.
|
||||
SVar:DBDestroy2:DB$ Destroy | ValidTgts$ Island | TgtPrompt$ Select target Island | SubAbility$ DBDestroyBlue | SpellDescription$ Destroy target Island and target blue creature.
|
||||
SVar:DBDestroyWhite:DB$ Destroy | ValidTgts$ Creature.White | TgtPrompt$ Select target white creature
|
||||
SVar:DBDestroyBlue:DB$ Destroy | ValidTgts$ Creature.Blue | TgtPrompt$ Select target blue creature
|
||||
SVar:DBDestroy1:DB$ Pump | ValidTgts$ Plains | TgtPrompt$ Select target Plains | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroyWhite | SpellDescription$ Destroy target Plains and target white creature.
|
||||
SVar:DBDestroy2:DB$ Pump | ValidTgts$ Island | TgtPrompt$ Select target Island | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroyBlue | SpellDescription$ Destroy target Island and target blue creature.
|
||||
SVar:DBDestroyWhite:DB$ Pump | ValidTgts$ Creature.White | TgtPrompt$ Select target white creature | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroyBlue:DB$ Pump | ValidTgts$ Creature.Blue | TgtPrompt$ Select target blue creature | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
AI:RemoveDeck:Random
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/reign_of_chaos.jpg
|
||||
Oracle:Choose one —\n• Destroy target Plains and target white creature.\n• Destroy target Island and target blue creature.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Spiteful Blow
|
||||
ManaCost:4 B B
|
||||
Types:Sorcery
|
||||
A:SP$ Destroy | Cost$ 4 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBDestroy | SpellDescription$ Destroy target creature and target land.
|
||||
SVar:DBDestroy:DB$ Destroy | ValidTgts$ Land | TgtPrompt$ Select target land
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/spiteful_blow.jpg
|
||||
A:SP$ Pump | Cost$ 4 B B | ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBLand | AILogic$ Destroy | Curse$ True | SpellDescription$ Destroy target creature and target land.
|
||||
SVar:DBLand:DB$ Pump | ValidTgts$ Land | TgtPrompt$ Select target land | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
Oracle:Destroy target creature and target land.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name:Stomp and Howl
|
||||
ManaCost:2 G
|
||||
Types:Sorcery
|
||||
A:SP$ Destroy | Cost$ 2 G | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | SpellDescription$ Destroy target artifact and target enchantment. | SubAbility$ DestroyEnch
|
||||
SVar:DestroyEnch:DB$ Destroy | ValidTgts$ Enchantment | TgtPrompt$ Select target enchantment
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/stomp_and_howl.jpg
|
||||
A:SP$ Pump | Cost$ 2 G | ValidTgts$ Artifact | TgtPrompt$ Select target artifact | AILogic$ Destroy | Curse$ True | SubAbility$ DestroyEnch | SpellDescription$ Destroy target artifact and target enchantment.
|
||||
SVar:DestroyEnch:DB$ Pump | ValidTgts$ Enchantment | TgtPrompt$ Select target enchantment | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
Oracle:Destroy target artifact and target enchantment.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Name:Volcanic Offering
|
||||
ManaCost:4 R
|
||||
Types:Instant
|
||||
A:SP$ Destroy | Cost$ 4 R | ValidTgts$ Land.nonBasic+YouDontCtrl | TgtPrompt$ Select target nonbasic land you don't control | SubAbility$ DBDestroyLand | SpellDescription$ Destroy target nonbasic land you don't control and target nonbasic land of an opponent's choice you don't control.
|
||||
SVar:DBDestroyLand:DB$ Destroy | TargetingPlayer$ Player.Opponent | ValidTgts$ Land.nonBasic+YouDontCtrl | TgtPrompt$ Select target nonbasic land the caster of this spell don't control | SubAbility$ DBDamage
|
||||
A:SP$ Pump | Cost$ 4 R | ValidTgts$ Land.nonBasic+YouDontCtrl | TgtPrompt$ Select target nonbasic land you don't control | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroyLand | SpellDescription$ Destroy target nonbasic land you don't control and target nonbasic land of an opponent's choice you don't control.
|
||||
SVar:DBDestroyLand:DB$ Pump | TargetingPlayer$ Player.Opponent | ValidTgts$ Land.nonBasic+YouDontCtrl | TgtPrompt$ Select target nonbasic land the caster of this spell don't control | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted | SubAbility$ DBDamage
|
||||
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Select target creature you don't control | NumDmg$ 7 | SubAbility$ DBDamage2 | SpellDescription$ CARDNAME deals 7 damage to target creature you don't control and 7 damage to target creature of an opponent's choice you don't control.
|
||||
SVar:DBDamage2:DB$ DealDamage | TargetingPlayer$ Player.Opponent | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Select target creature the caster of this spell don't control | NumDmg$ 7
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/volcanic_offering.jpg
|
||||
Oracle:Destroy target nonbasic land you don't control and target nonbasic land of an opponent's choice you don't control.\nVolcanic Offering deals 7 damage to target creature you don't control and 7 damage to target creature of an opponent's choice you don't control.
|
||||
|
||||
@@ -3,8 +3,8 @@ ManaCost:2 B
|
||||
Types:Creature Snake Wall
|
||||
PT:2/4
|
||||
K:Defender
|
||||
A:AB$ Destroy | Cost$ 3 | Defined$ Self | Activator$ Player | SubAbility$ SnakeBite | SpellDescription$ Destroy CARDNAME and target creature it's blocking. Any player may activate this ability.
|
||||
SVar:SnakeBite:DB$ Destroy | ValidTgts$ Creature.blockedBySource
|
||||
A:AB$ Pump | Cost$ 3 | Defined$ Self | Activator$ Player | AILogic$ Destroy | Curse$ True | SubAbility$ SnakeBite | SpellDescription$ Destroy CARDNAME and target creature it's blocking. Any player may activate this ability.
|
||||
SVar:SnakeBite:DB$ Pump | ValidTgts$ Creature.blockedBySource | AILogic$ Destroy | Curse$ True | SubAbility$ DBDestroy
|
||||
SVar:DBDestroy:DB$ Destroy | Defined$ Targeted
|
||||
AI:RemoveDeck:All
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/wall_of_vipers.jpg
|
||||
Oracle:Defender (This creature can't attack.)\n{3}: Destroy Wall of Vipers and target creature it's blocking. Any player may activate this ability.
|
||||
|
||||
@@ -673,7 +673,7 @@ public class HumanPlay {
|
||||
final Card offering = ability.getSacrificedAsOffering();
|
||||
offering.setUsedToPay(false);
|
||||
if (!manaInputCancelled) {
|
||||
game.getAction().sacrifice(offering, ability, table);
|
||||
game.getAction().sacrifice(offering, ability, table, null);
|
||||
}
|
||||
ability.resetSacrificedAsOffering();
|
||||
}
|
||||
@@ -681,7 +681,7 @@ public class HumanPlay {
|
||||
final Card emerge = ability.getSacrificedAsEmerge();
|
||||
emerge.setUsedToPay(false);
|
||||
if (!manaInputCancelled) {
|
||||
game.getAction().sacrifice(emerge, ability, table);
|
||||
game.getAction().sacrifice(emerge, ability, table, null);
|
||||
}
|
||||
ability.resetSacrificedAsEmerge();
|
||||
}
|
||||
@@ -785,7 +785,7 @@ public class HumanPlay {
|
||||
if (ability.getSacrificedAsOffering() != null) {
|
||||
System.out.println("Finishing up Offering");
|
||||
offering.setUsedToPay(false);
|
||||
activator.getGame().getAction().sacrifice(offering, ability, null);
|
||||
activator.getGame().getAction().sacrifice(offering, ability, null, null);
|
||||
ability.resetSacrificedAsOffering();
|
||||
}
|
||||
}
|
||||
@@ -796,7 +796,7 @@ public class HumanPlay {
|
||||
if (ability.getSacrificedAsEmerge() != null) {
|
||||
System.out.println("Finishing up Emerge");
|
||||
emerge.setUsedToPay(false);
|
||||
activator.getGame().getAction().sacrifice(emerge, ability, null);
|
||||
activator.getGame().getAction().sacrifice(emerge, ability, null, null);
|
||||
ability.resetSacrificedAsEmerge();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user