mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
refactor: improved search in stack
This commit is contained in:
parent
26adccdfd5
commit
384ce67cc3
20 changed files with 101 additions and 119 deletions
|
|
@ -398,7 +398,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
}
|
||||
|
||||
protected void resolve(SimulationNode2 node, int depth, Game game) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
if (stackObject == null) {
|
||||
throw new IllegalStateException("Catch empty stack on resolve (something wrong with sim code)");
|
||||
}
|
||||
if (stackObject instanceof StackAbility) {
|
||||
// AI hint for search effects (calc all possible cards for best score)
|
||||
SearchEffect effect = getSearchEffect((StackAbility) stackObject);
|
||||
|
|
|
|||
|
|
@ -638,8 +638,8 @@ public class HumanPlayer extends PlayerImpl {
|
|||
// Check check if the spell being paid for cares about the color of mana being paid
|
||||
// See: https://github.com/magefree/mage/issues/9070
|
||||
boolean caresAboutManaColor = false;
|
||||
if (!game.getStack().isEmpty() && game.getStack().getFirst() instanceof Spell) {
|
||||
Spell spellBeingCast = (Spell) game.getStack().getFirst();
|
||||
if (game.getStack().getFirstOrNull() instanceof Spell) {
|
||||
Spell spellBeingCast = (Spell) game.getStack().getFirstOrNull();
|
||||
if (!spellBeingCast.isResolving() && spellBeingCast.getControllerId().equals(this.getId())) {
|
||||
CardImpl card = (CardImpl) game.getCard(spellBeingCast.getSourceId());
|
||||
caresAboutManaColor = card.caresAboutManaColor(game);
|
||||
|
|
|
|||
|
|
@ -68,10 +68,7 @@ class AstralDriftTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.getState().getStack().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
StackObject item = game.getState().getStack().getFirst();
|
||||
StackObject item = game.getState().getStack().getFirstOrNull();
|
||||
if (!(item instanceof StackAbility
|
||||
&& item.getStackAbility() instanceof CyclingAbility)) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ class CobraTrapWatcher extends Watcher {
|
|||
if (event.getType() == GameEvent.EventType.DESTROYED_PERMANENT) {
|
||||
Permanent perm = game.getPermanentOrLKIBattlefield(event.getTargetId()); // can regenerate or be indestructible
|
||||
if (perm != null && !perm.isCreature(game)) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject spell = game.getStack().getStackObject(event.getSourceId());
|
||||
if (spell != null && game.getOpponents(perm.getControllerId()).contains(spell.getControllerId())) {
|
||||
players.add(perm.getControllerId());
|
||||
|
|
@ -86,7 +85,6 @@ class CobraTrapWatcher extends Watcher {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
|
|
@ -10,20 +8,20 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class RainOfGore extends CardImpl {
|
||||
|
||||
public RainOfGore(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}{R}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}{R}");
|
||||
|
||||
|
||||
// If a spell or ability would cause its controller to gain life, that player loses that much life instead.
|
||||
|
|
@ -74,10 +72,7 @@ class RainOfGoreEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
return stackObject.isControlledBy(event.getPlayerId());
|
||||
}
|
||||
return false;
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
return stackObject != null && stackObject.isControlledBy(event.getPlayerId());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,13 +120,8 @@ enum SoulSculptorCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
if (stackObject != null) {
|
||||
return !stackObject.getCardType(game).contains(CardType.CREATURE);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
return stackObject != null && !stackObject.getCardType(game).contains(CardType.CREATURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -94,7 +95,9 @@ class SoulfireGrandMasterCastFromHandReplacementEffect extends ReplacementEffect
|
|||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Spell spell = (Spell) game.getStack().getFirst();
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
if (stackObject instanceof Spell) {
|
||||
Spell spell = (Spell) stackObject;
|
||||
if (!spell.isCopy() && !spell.isCountered()) {
|
||||
Card sourceCard = game.getCard(spellId);
|
||||
if (sourceCard != null && Zone.STACK.equals(game.getState().getZone(spellId))) {
|
||||
|
|
@ -106,6 +109,8 @@ class SoulfireGrandMasterCastFromHandReplacementEffect extends ReplacementEffect
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -134,8 +139,8 @@ class SoulfireGrandMasterCastFromHandReplacementEffect extends ReplacementEffect
|
|||
if (zEvent.getFromZone() == Zone.STACK
|
||||
&& zEvent.getToZone() == Zone.GRAVEYARD
|
||||
&& event.getTargetId().equals(spellId)) {
|
||||
if (game.getStack().getFirst() instanceof Spell) {
|
||||
Card cardOfSpell = ((Spell) game.getStack().getFirst()).getCard();
|
||||
if (game.getStack().getFirstOrNull() instanceof Spell) {
|
||||
Card cardOfSpell = ((Spell) game.getStack().getFirstOrNull()).getCard();
|
||||
return cardOfSpell.getMainCard().getId().equals(spellId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,12 +99,10 @@ enum FirstSpellCastFromNotHandEachTurnCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (game.getStack().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
TheTwelfthDoctorWatcher watcher = game.getState().getWatcher(TheTwelfthDoctorWatcher.class);
|
||||
StackObject so = game.getStack().getFirst();
|
||||
return watcher != null
|
||||
StackObject so = game.getStack().getFirstOrNull();
|
||||
return so != null
|
||||
&& watcher != null
|
||||
&& TheTwelfthDoctorWatcher.checkSpell(so, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,12 +74,11 @@ class ValiantRescuerTriggeredAbility extends TriggeredAbilityImpl {
|
|||
ValiantRescuerWatcher watcher = game.getState().getWatcher(ValiantRescuerWatcher.class);
|
||||
if (watcher == null
|
||||
|| !watcher.checkSpell(event.getPlayerId(), event.getSourceId())
|
||||
|| game.getState().getStack().isEmpty()
|
||||
|| !event.getPlayerId().equals(this.getControllerId())
|
||||
|| event.getSourceId().equals(this.getSourceId())) {
|
||||
return false;
|
||||
}
|
||||
StackObject item = game.getState().getStack().getFirst();
|
||||
StackObject item = game.getState().getStack().getFirstOrNull();
|
||||
return item instanceof StackAbility
|
||||
&& item.getStackAbility() instanceof CyclingAbility;
|
||||
}
|
||||
|
|
@ -106,11 +105,10 @@ class ValiantRescuerWatcher extends Watcher {
|
|||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() != GameEvent.EventType.ACTIVATED_ABILITY
|
||||
|| game.getState().getStack().isEmpty()) {
|
||||
if (event.getType() != GameEvent.EventType.ACTIVATED_ABILITY) {
|
||||
return;
|
||||
}
|
||||
StackObject item = game.getState().getStack().getFirst();
|
||||
StackObject item = game.getState().getStack().getFirstOrNull();
|
||||
if (item instanceof StackAbility
|
||||
&& item.getStackAbility() instanceof CyclingAbility) {
|
||||
playerMap.computeIfAbsent(event.getPlayerId(), u -> new HashMap<>());
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ package mage.cards.w;
|
|||
|
||||
import mage.MageInt;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.keyword.CascadeAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -11,14 +14,12 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
|
|
@ -96,12 +97,10 @@ enum FirstSpellCastFromExileEachTurnCondition implements Condition {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (game.getStack().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
WildMagicSorcererWatcher watcher = game.getState().getWatcher(WildMagicSorcererWatcher.class);
|
||||
StackObject so = game.getStack().getFirst();
|
||||
return watcher != null
|
||||
StackObject so = game.getStack().getFirstOrNull();
|
||||
return so != null
|
||||
&& watcher != null
|
||||
&& WildMagicSorcererWatcher.checkSpell(so, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public class DisguiseTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dog Walker using Disguise");
|
||||
runCode("face up on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
Assert.assertEquals("stack, server - can't find spell", 1, currentGame.getStack().size());
|
||||
SpellAbility spellAbility = (SpellAbility) currentGame.getStack().getFirst().getStackAbility();
|
||||
SpellAbility spellAbility = (SpellAbility) currentGame.getStack().getFirstOrNull().getStackAbility();
|
||||
Assert.assertEquals("stack, server - can't find spell", "Cast Dog Walker using Disguise", spellAbility.getName());
|
||||
CardView spellView = getGameView(playerA).getStack().values().stream().findFirst().orElse(null);
|
||||
Assert.assertNotNull("stack, client: can't find spell", spellView);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class DisturbTest extends CardTestPlayerBase {
|
|||
checkStackObject("on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb", 1);
|
||||
runCode("check stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
// Stack must contain another card side, so spell/card characteristics must be diff from main side (only mana value is same)
|
||||
Spell spell = (Spell) game.getStack().getFirst();
|
||||
Spell spell = (Spell) game.getStack().getFirstOrNull();
|
||||
Assert.assertEquals("Hook-Haunt Drifter", spell.getName());
|
||||
Assert.assertEquals(1, spell.getCardType(game).size());
|
||||
Assert.assertEquals(CardType.CREATURE, spell.getCardType(game).get(0));
|
||||
|
|
@ -91,7 +91,7 @@ public class DisturbTest extends CardTestPlayerBase {
|
|||
checkStackObject("on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Waildrifter using Disturb", 1);
|
||||
runCode("check stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
// Stack must contain another card side, so spell/card characteristics must be diff from main side (only mana value is same)
|
||||
Spell spell = (Spell) game.getStack().getFirst();
|
||||
Spell spell = (Spell) game.getStack().getFirstOrNull();
|
||||
Assert.assertEquals("Waildrifter", spell.getName());
|
||||
Assert.assertEquals(1, spell.getCardType(game).size());
|
||||
Assert.assertEquals(CardType.CREATURE, spell.getCardType(game).get(0));
|
||||
|
|
@ -187,7 +187,7 @@ public class DisturbTest extends CardTestPlayerBase {
|
|||
// cast with disturb
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Hook-Haunt Drifter using Disturb");
|
||||
runCode("check stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
Spell spell = (Spell) game.getStack().getFirst();
|
||||
Spell spell = (Spell) game.getStack().getFirstOrNull();
|
||||
Assert.assertEquals("mana value must be from main side", 2, spell.getManaValue());
|
||||
Assert.assertEquals("mana cost to pay must be modified", "{U}", spell.getSpellAbility().getManaCostsToPay().getText());
|
||||
});
|
||||
|
|
|
|||
|
|
@ -334,11 +334,8 @@ public class TestPlayer implements Player {
|
|||
return true;
|
||||
} else if (groups[2].startsWith("spellOnTopOfStack=")) {
|
||||
String spellOnTopOFStack = groups[2].substring(18);
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
return stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack);
|
||||
}
|
||||
return false;
|
||||
} else if (groups[2].startsWith("manaInPool=")) {
|
||||
String manaInPool = groups[2].substring(11);
|
||||
int amountOfMana = Integer.parseInt(manaInPool);
|
||||
|
|
|
|||
|
|
@ -53,12 +53,10 @@ public abstract class SpecialAction extends ActivatedAbilityImpl {
|
|||
if (isManaAction()) {
|
||||
// limit play mana abilities by steps
|
||||
int currentStepOrder = 0;
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
if (stackObject instanceof Spell) {
|
||||
currentStepOrder = ((Spell) stackObject).getCurrentActivatingManaAbilitiesStep().getStepOrder();
|
||||
}
|
||||
}
|
||||
if (currentStepOrder > manaAbility.useOnActivationManaAbilityStep().getStepOrder()) {
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,7 @@ public class CycleAllTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.getState().getStack().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
StackObject item = game.getState().getStack().getFirst();
|
||||
StackObject item = game.getState().getStack().getFirstOrNull();
|
||||
return item instanceof StackAbility
|
||||
&& item.getStackAbility() instanceof CyclingAbility;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,12 +42,11 @@ public class CycleControllerTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.getState().getStack().isEmpty()
|
||||
|| !event.getPlayerId().equals(this.getControllerId())
|
||||
if (!event.getPlayerId().equals(this.getControllerId())
|
||||
|| (event.getSourceId().equals(this.getSourceId()) && excludeSource)) {
|
||||
return false;
|
||||
}
|
||||
StackObject item = game.getState().getStack().getFirst();
|
||||
StackObject item = game.getState().getStack().getFirstOrNull();
|
||||
return item instanceof StackAbility
|
||||
&& item.getStackAbility() instanceof CyclingAbility;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ public enum SunburstCount implements DynamicValue {
|
|||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
int count = 0;
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject spell = game.getStack().getFirst();
|
||||
if (spell instanceof Spell && ((Spell) spell).getSourceId().equals(sourceAbility.getSourceId())) {
|
||||
|
||||
StackObject spell = game.getStack().getFirstOrNull();
|
||||
if (spell instanceof Spell && spell.getSourceId().equals(sourceAbility.getSourceId())) {
|
||||
Mana mana = ((Spell) spell).getSpellAbility().getManaCostsToPay().getUsedManaToPay();
|
||||
if (mana.getBlack() > 0) {
|
||||
count++;
|
||||
|
|
@ -38,7 +38,6 @@ public enum SunburstCount implements DynamicValue {
|
|||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -513,14 +513,11 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
// If the top card of your library changes while you’re casting a spell, playing a land, or activating an ability,
|
||||
// you can’t look at the new top card until you finish doing so. This means that if you cast the top card of
|
||||
// your library, you can’t look at the next one until you’re done paying for that spell. (2019-05-03)
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
return !(stackObject instanceof Spell)
|
||||
|| !Zone.LIBRARY.equals(((Spell) stackObject).getFromZone())
|
||||
|| stackObject.getStackAbility().getManaCostsToPay().isPaid(); // mana payment finished
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContinuousEffect setText(String staticText) {
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
|
|||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
// check if player is in the process of playing spell costs and they are no longer allowed to use
|
||||
// activated mana abilities (e.g. because they started to use improvise or convoke)
|
||||
if (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
if (stackObject instanceof Spell) {
|
||||
switch (((Spell) stackObject).getCurrentActivatingManaAbilitiesStep()) {
|
||||
case BEFORE:
|
||||
|
|
@ -55,8 +54,6 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl
|
|||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.canActivate(playerId, game);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,16 @@ public class SpellStack extends ArrayDeque<StackObject> {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated // must use getFirstOrNull instead
|
||||
public StackObject getFirst() {
|
||||
return super.getFirst();
|
||||
}
|
||||
|
||||
public StackObject getFirstOrNull() {
|
||||
return this.isEmpty() ? null : this.getFirst();
|
||||
}
|
||||
|
||||
public boolean remove(StackObject object, Game game) {
|
||||
for (StackObject spell : this) {
|
||||
if (spell.getId().equals(object.getId())) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue