* Activate spell's ability on the stack - fixed that it can't be activated by humans (example: Lightning Storm);

This commit is contained in:
Oleg Agafonov 2020-12-22 17:13:00 +04:00
parent 3f7b26f60b
commit 0ac4a9d87a
4 changed files with 48 additions and 26 deletions

View file

@ -88,7 +88,7 @@ public final class GamePanel extends javax.swing.JPanel {
private String chosenHandKey = "You"; private String chosenHandKey = "You";
private boolean smallMode = false; private boolean smallMode = false;
private boolean initialized = false; private boolean initialized = false;
private skipButtonsList skipButtons = new skipButtonsList(); private final skipButtonsList skipButtons = new skipButtonsList();
private boolean menuNameSet = false; private boolean menuNameSet = false;
private boolean handCardsOfOpponentAvailable = false; private boolean handCardsOfOpponentAvailable = false;
@ -102,7 +102,7 @@ public final class GamePanel extends javax.swing.JPanel {
private boolean initComponents; private boolean initComponents;
private Timer resizeTimer; private final Timer resizeTimer;
private enum PopUpMenuType { private enum PopUpMenuType {
TRIGGER_ORDER TRIGGER_ORDER
@ -1289,7 +1289,11 @@ public final class GamePanel extends javax.swing.JPanel {
if (needChoosen.contains(card.getKey())) { if (needChoosen.contains(card.getKey())) {
card.getValue().setSelected(true); 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 // Event listener for the ShowCardsDialog
private Listener<Event> getShowCardsEventListener(final ShowCardsDialog dialog) { private Listener<Event> getShowCardsEventListener(final ShowCardsDialog dialog) {
return (Listener<Event>) event -> { return event -> {
if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) { if (event.getEventType() == ClientEventType.SHOW_POP_UP_MENU) {
if (event.getComponent() != null && event.getComponent() instanceof CardPanel) { if (event.getComponent() != null && event.getComponent() instanceof CardPanel) {
JPopupMenu menu = ((CardPanel) event.getComponent()).getPopupMenu(); JPopupMenu menu = ((CardPanel) event.getComponent()).getPopupMenu();

View file

@ -143,21 +143,24 @@ public class PlayerPanelExt extends javax.swing.JPanel {
} }
private boolean isCardsPlayable(Collection<CardView> cards, GameView gameView, Set<UUID> possibleTargets) { private boolean isCardsPlayable(Collection<CardView> cards, GameView gameView, Set<UUID> possibleTargets) {
if (cards != null) { if (cards == null || gameView == null) {
// can play return false;
if (gameView != null && gameView.getCanPlayObjects() != null && !gameView.getCanPlayObjects().isEmpty()) { }
for (CardView card : cards) {
if (gameView.getCanPlayObjects().containsKey(card.getId())) { // can play
return true; 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) { // can select
if (possibleTargets.contains(card.getId())) { if (possibleTargets != null && !possibleTargets.isEmpty()) {
return true; for (CardView card : cards) {
} if (possibleTargets.contains(card.getId())) {
return true;
} }
} }
} }

View file

@ -1,7 +1,5 @@
package mage.cards.l; package mage.cards.l;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl; import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
@ -21,11 +19,12 @@ import mage.counters.CounterType;
import mage.filter.common.FilterLandCard; import mage.filter.common.FilterLandCard;
import mage.game.Game; import mage.game.Game;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.target.common.TargetCardInHand;
import mage.target.common.TargetAnyTarget; import mage.target.common.TargetAnyTarget;
import mage.target.common.TargetCardInHand;
import java.util.UUID;
/** /**
*
* @author LevelX2 * @author LevelX2
*/ */
public final class LightningStorm extends CardImpl { 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"); 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().addEffect(effect);
this.getSpellAbility().addTarget(new TargetAnyTarget()); 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. // 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, SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.STACK,
new LightningStormAddCounterEffect(), new LightningStormAddCounterEffect(),
@ -115,7 +115,8 @@ class LightningStormAddCounterEffect extends OneShotEffect {
Spell spell = game.getStack().getSpell(source.getSourceId()); Spell spell = game.getStack().getSpell(source.getSourceId());
if (spell != null) { if (spell != null) {
spell.addCounters(CounterType.CHARGE.createInstance(2), source, game); 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; return false;
} }

View file

@ -44,7 +44,6 @@ import mage.game.*;
import mage.game.combat.CombatGroup; import mage.game.combat.CombatGroup;
import mage.game.command.CommandObject; import mage.game.command.CommandObject;
import mage.game.events.*; import mage.game.events.*;
import mage.game.events.GameEvent.EventType;
import mage.game.match.MatchPlayer; import mage.game.match.MatchPlayer;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentCard;
@ -1568,7 +1567,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override @Override
public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) { public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>(); LinkedHashMap<UUID, ActivatedAbility> 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) { if (object instanceof StackAbility || object == null) {
return useable; return useable;
} }
@ -1594,10 +1594,15 @@ public abstract class PlayerImpl implements Player, Serializable {
needId1 = object.getId(); needId1 = object.getId();
needId2 = ((AdventureCardSpell) object).getParentCard().getId(); needId2 = ((AdventureCardSpell) object).getParentCard().getId();
needId3 = object.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 { } else {
needId1 = object.getId(); needId1 = object.getId();
needId2 = object.getId(); needId2 = null;
needId3 = object.getId(); needId3 = null;
} }
// workaround to find all abilities first and filter it for one object // 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 result for next flip coint try (can be contolled in tests)
*
* @return * @return
*/ */
@Override @Override
@ -3708,6 +3714,14 @@ public abstract class PlayerImpl implements Player, Serializable {
UUID mainCardId = card.getMainCard().getId(); UUID mainCardId = card.getMainCard().getId();
playableObjects.put(mainCardId, playableObjects.getOrDefault(mainCardId, 0) + 1); 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; return playableObjects;