Connive ability - fixed game error on usage (NPE), fixed game freeze on disconnect, fixed miss LKI related code;

This commit is contained in:
Oleg Agafonov 2023-05-09 15:36:33 +04:00
parent 3c66dc8706
commit a315171ca4
6 changed files with 54 additions and 21 deletions

View file

@ -73,8 +73,8 @@ class KamizConniveEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
return permanent != null && ConniveSourceEffect.connive(permanent, 1, source, game); return ConniveSourceEffect.connive(permanent, 1, source, game);
} }
} }

View file

@ -116,10 +116,18 @@ class LethalSchemeEffect extends OneShotEffect {
player.choose(Outcome.Neutral, choiceForThisLoop, game); player.choose(Outcome.Neutral, choiceForThisLoop, game);
String choice = choiceForThisLoop.getChoice(); String choice = choiceForThisLoop.getChoice();
Permanent choicePermanent = permanents.stream().filter(permanent -> permanent.getIdName().equals(choice)).findFirst().get(); Permanent choicePermanent = permanents
.stream()
ConniveSourceEffect.connive(choicePermanent, 1, source, game); .filter(permanent -> permanent.getIdName().equals(choice))
permanents.remove(choicePermanent); .findFirst()
.orElse(null);
if (choicePermanent != null) {
ConniveSourceEffect.connive(choicePermanent, 1, source, game);
permanents.remove(choicePermanent);
} else {
// no choices, e.g. disconnection
break;
}
} }
} }
return true; return true;

View file

@ -71,10 +71,10 @@ class MaskOfTheSchemerEffect extends OneShotEffect {
if (equipment == null || damage < 1) { if (equipment == null || damage < 1) {
return false; return false;
} }
Permanent permanent = game.getPermanent(equipment.getAttachedTo()); Permanent permanent = game.getPermanentOrLKIBattlefield(equipment.getAttachedTo());
if (permanent == null) { if (permanent == null) {
return false; return false;
} }
return permanent != null && ConniveSourceEffect.connive(permanent, damage, source, game); return ConniveSourceEffect.connive(permanent, damage, source, game);
} }
} }

View file

@ -79,8 +79,8 @@ class ObscuraConfluenceConniveEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
return permanent != null && ConniveSourceEffect.connive(permanent, 1, source, game); return ConniveSourceEffect.connive(permanent, 1, source, game);
} }
} }

View file

@ -85,7 +85,7 @@ class RaffineSchemingSeerEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
if (permanent == null) { if (permanent == null) {
return false; return false;
} }

View file

@ -14,12 +14,31 @@ import mage.players.Player;
import mage.util.CardUtil; import mage.util.CardUtil;
/** /**
* 701.47. Connive
* <p>
* 701.47a Certain abilities instruct a permanent to connive. To do so, that permanents controller draws a card,
* then discards a card. If a nonland card is discarded this way, that player puts a +1/+1 counter on the
* conniving permanent.
* <p>
* 701.47b A permanent connives after the process described in rule 701.47a is complete, even if some or
* all of those actions were impossible.
* <p>
* 701.47c If a permanent changes zones before an effect causes it to connive, its last known information is
* used to determine which object connived and who controlled it.
* <p>
* 701.47d If multiple permanents are instructed to connive at the same time, the first player in APNAP order
* who controls one or more of those permanents chooses one of them and it connives. Then if any permanents
* remain on the battlefield which have been instructed to connive and have not done so, this process is repeated.
* <p>
* 701.47e Connive N is a variant of connive. The permanents controller draws N cards, discards N cards, then
* puts a number of +1/+1 counters on the permanent equal to the number of nonland cards discarded this way.
*
* @author TheElk801 * @author TheElk801
*/ */
public class ConniveSourceEffect extends OneShotEffect { public class ConniveSourceEffect extends OneShotEffect {
private final String selfName; private final String selfName;
private final ReflexiveTriggeredAbility ability; private final ReflexiveTriggeredAbility ability; // apply ability after connived
public ConniveSourceEffect() { public ConniveSourceEffect() {
this("it"); this("it");
@ -48,31 +67,37 @@ public class ConniveSourceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = source.getSourcePermanentIfItStillExists(game); // 701.47c If a permanent changes zones before an effect causes it to connive,
// its last known information is used to determine which object connived and who controlled it.
Permanent permanent = source.getSourcePermanentOrLKI(game);
boolean connived = connive(permanent, 1, source, game); boolean connived = connive(permanent, 1, source, game);
if (ability != null) { if (ability != null && connived) {
game.fireReflexiveTriggeredAbility(ability, source); game.fireReflexiveTriggeredAbility(ability, source);
} }
return connived || ability != null; return connived;
} }
/**
* @param permanent must use game.getPermanentOrLKIBattlefield in parent method due rules
* @param amount
* @param source
* @param game
* @return
*/
public static boolean connive(Permanent permanent, int amount, Ability source, Game game) { public static boolean connive(Permanent permanent, int amount, Ability source, Game game) {
if (amount < 1) { if (amount < 1) {
return false; return false;
} }
boolean permanentStillOnBattlefield;
if (permanent == null) { if (permanent == null) {
// If the permanent was killed, get last known information return false;
permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
permanentStillOnBattlefield = false;
} else {
permanentStillOnBattlefield = true;
} }
boolean permanentStillOnBattlefield = game.getState().getZone(permanent.getId()) == Zone.BATTLEFIELD;
Player player = game.getPlayer(permanent.getControllerId()); Player player = game.getPlayer(permanent.getControllerId());
if (player == null) { if (player == null) {
return false; return false;
} }
player.drawCards(amount, source, game); player.drawCards(amount, source, game);
int counters = player int counters = player
.discard(amount, false, false, source, game) .discard(amount, false, false, source, game)