From 0ac4a9d87a02295986235e3d96836044ced3dcb1 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 22 Dec 2020 17:13:00 +0400 Subject: [PATCH] * Activate spell's ability on the stack - fixed that it can't be activated by humans (example: Lightning Storm); --- .../main/java/mage/client/game/GamePanel.java | 12 +++++--- .../java/mage/client/game/PlayerPanelExt.java | 29 ++++++++++--------- .../src/mage/cards/l/LightningStorm.java | 11 +++---- .../main/java/mage/players/PlayerImpl.java | 22 +++++++++++--- 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 0d6ecf38fa..f04ffbd738 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -88,7 +88,7 @@ public final class GamePanel extends javax.swing.JPanel { private String chosenHandKey = "You"; private boolean smallMode = false; private boolean initialized = false; - private skipButtonsList skipButtons = new skipButtonsList(); + private final skipButtonsList skipButtons = new skipButtonsList(); private boolean menuNameSet = false; private boolean handCardsOfOpponentAvailable = false; @@ -102,7 +102,7 @@ public final class GamePanel extends javax.swing.JPanel { private boolean initComponents; - private Timer resizeTimer; + private final Timer resizeTimer; private enum PopUpMenuType { TRIGGER_ORDER @@ -1289,7 +1289,11 @@ public final class GamePanel extends javax.swing.JPanel { if (needChoosen.contains(card.getKey())) { card.getValue().setSelected(true); } - // play from stack unsupported + // users can activate abilities of the spell on the stack (example: Lightning Storm); + if (needPlayable.containsKey(card.getKey())) { + card.getValue().setPlayable(true); + card.getValue().setPlayableAmount(needPlayable.get(card.getKey())); + } } } @@ -2537,7 +2541,7 @@ public final class GamePanel extends javax.swing.JPanel { // Event listener for the ShowCardsDialog private Listener getShowCardsEventListener(final ShowCardsDialog dialog) { - return (Listener) event -> { + return event -> { if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) { if (event.getComponent() != null && event.getComponent() instanceof CardPanel) { JPopupMenu menu = ((CardPanel) event.getComponent()).getPopupMenu(); diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java index 2ba6bc104d..d8004aecca 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -143,21 +143,24 @@ public class PlayerPanelExt extends javax.swing.JPanel { } private boolean isCardsPlayable(Collection cards, GameView gameView, Set possibleTargets) { - if (cards != null) { - // can play - if (gameView != null && gameView.getCanPlayObjects() != null && !gameView.getCanPlayObjects().isEmpty()) { - for (CardView card : cards) { - if (gameView.getCanPlayObjects().containsKey(card.getId())) { - return true; - } + if (cards == null || gameView == null) { + return false; + } + + // can play + if (gameView.getCanPlayObjects() != null && !gameView.getCanPlayObjects().isEmpty()) { + for (CardView card : cards) { + if (gameView.getCanPlayObjects().containsKey(card.getId())) { + return true; } } - // can select - if (possibleTargets != null && !possibleTargets.isEmpty()) { - for (CardView card : cards) { - if (possibleTargets.contains(card.getId())) { - return true; - } + } + + // can select + if (possibleTargets != null && !possibleTargets.isEmpty()) { + for (CardView card : cards) { + if (possibleTargets.contains(card.getId())) { + return true; } } } diff --git a/Mage.Sets/src/mage/cards/l/LightningStorm.java b/Mage.Sets/src/mage/cards/l/LightningStorm.java index cb0981aa33..835bd27844 100644 --- a/Mage.Sets/src/mage/cards/l/LightningStorm.java +++ b/Mage.Sets/src/mage/cards/l/LightningStorm.java @@ -1,7 +1,5 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; @@ -21,11 +19,12 @@ import mage.counters.CounterType; import mage.filter.common.FilterLandCard; import mage.game.Game; import mage.game.stack.Spell; -import mage.target.common.TargetCardInHand; import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetCardInHand; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class LightningStorm extends CardImpl { @@ -38,6 +37,7 @@ public final class LightningStorm extends CardImpl { effect.setText("{this} deals X damage to any target, where X is 3 plus the number of charge counters on it"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTarget()); + // Discard a land card: Put two charge counters on Lightning Storm. You may choose a new target for it. Any player may activate this ability but only if Lightning Storm is on the stack. SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.STACK, new LightningStormAddCounterEffect(), @@ -115,7 +115,8 @@ class LightningStormAddCounterEffect extends OneShotEffect { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { spell.addCounters(CounterType.CHARGE.createInstance(2), source, game); - return spell.chooseNewTargets(game, ((ActivatedAbilityImpl) source).getActivatorId(), false, false, null); + spell.chooseNewTargets(game, ((ActivatedAbilityImpl) source).getActivatorId(), false, false, null); + return true; } return false; } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index cc8030fe3c..38e5e3ae12 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -44,7 +44,6 @@ import mage.game.*; import mage.game.combat.CombatGroup; import mage.game.command.CommandObject; import mage.game.events.*; -import mage.game.events.GameEvent.EventType; import mage.game.match.MatchPlayer; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -1568,7 +1567,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public LinkedHashMap getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap<>(); - // It may not be possible to activate abilities of stack abilities + // stack abilities - can't activate anything + // spell ability - can activate additional abilities (example: "Lightning Storm") if (object instanceof StackAbility || object == null) { return useable; } @@ -1594,10 +1594,15 @@ public abstract class PlayerImpl implements Player, Serializable { needId1 = object.getId(); needId2 = ((AdventureCardSpell) object).getParentCard().getId(); needId3 = object.getId(); + } else if (object instanceof Spell) { + // example: activate Lightning Storm's ability from the spell on the stack + needId1 = object.getId(); + needId2 = ((Spell) object).getCard().getId(); + needId3 = null; } else { needId1 = object.getId(); - needId2 = object.getId(); - needId3 = object.getId(); + needId2 = null; + needId3 = null; } // workaround to find all abilities first and filter it for one object @@ -2791,6 +2796,7 @@ public abstract class PlayerImpl implements Player, Serializable { /** * Return result for next flip coint try (can be contolled in tests) + * * @return */ @Override @@ -3708,6 +3714,14 @@ public abstract class PlayerImpl implements Player, Serializable { UUID mainCardId = card.getMainCard().getId(); playableObjects.put(mainCardId, playableObjects.getOrDefault(mainCardId, 0) + 1); } + + // spell on stack can have activated abilities, + // so mark it as playable too (users must able to clicks on stack objects) + // example: Lightning Storm + Spell spell = game.getSpell(ability.getSourceId()); + if (spell != null) { + playableObjects.put(spell.getId(), playableObjects.getOrDefault(spell.getId(), 0) + 1); + } } } return playableObjects;