foul-magics/Mage/src/main/java/mage/abilities/effects/common/SacrificeAllEffect.java
Oleg Agafonov 133e4fe425
other: reworked target selection: (#13638)
- WIP: AI and multi targets, human and X=0 use cases, human and impossible targets use cases;
- improved stability and shared logic (related to #13606, #11134, #11666, continue from a53eb66b58, close #13617, close #13613);
- improved test logs and debug info to show more target info on errors;
- improved test framework to support multiple addTarget calls;
- improved test framework to find bad commands order for targets (related to #11666);
- fixed game freezes on auto-choice usages with disconnected or under control players (related to #11285);
- gui, game: fixed that player doesn't mark avatar as selected/green in "up to" targeting;
- gui, game: fixed small font in some popup messages on big screens (related to #969);
- gui, game: added min targets info for target selection dialog;
- for devs: added new cheat option to call and test any game dialog (define own dialogs, targets, etc in HumanDialogsTester);
- for devs: now tests require complete an any or up to target selection by addTarget + TestPlayer.TARGET_SKIP or setChoice + TestPlayer.CHOICE_SKIP (if not all max/possible targets used);
- for devs: added detail targets info for activate/trigger/cast, can be useful to debug unit tests, auto-choose or AI (see DebugUtil.GAME_SHOW_CHOOSE_TARGET_LOGS)
2025-05-16 13:55:54 +04:00

146 lines
5 KiB
Java

package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetSacrifice;
import mage.util.CardUtil;
import java.util.*;
/**
* @author BetaSteward_at_googlemail.com, JayDi85
*/
public class SacrificeAllEffect extends OneShotEffect {
private final DynamicValue amount;
private final FilterPermanent filter;
private final boolean onlyOpponents;
private static final String VALUE_KEY = "sacrificeAllEffect_permanentsList";
/**
* Each player sacrifices a permanent
*
* @param filter can be generic, will automatically add article and necessary sacrifice predicates
*/
public SacrificeAllEffect(FilterPermanent filter) {
this(1, filter);
}
/**
* Each player sacrifices N permanents
*
* @param filter can be generic, will automatically add necessary sacrifice predicates
*/
public SacrificeAllEffect(int amount, FilterPermanent filter) {
this(StaticValue.get(amount), filter);
}
/**
* Each player sacrifices X permanents
*
* @param filter can be generic, will automatically add necessary sacrifice predicates
*/
public SacrificeAllEffect(DynamicValue amount, FilterPermanent filter) {
this(amount, filter, false);
}
protected SacrificeAllEffect(DynamicValue amount, FilterPermanent filter, boolean onlyOpponents) {
super(Outcome.Sacrifice);
this.amount = amount;
this.filter = filter;
this.onlyOpponents = onlyOpponents;
setText();
}
protected SacrificeAllEffect(final SacrificeAllEffect effect) {
super(effect);
this.amount = effect.amount;
this.filter = effect.filter.copy();
this.onlyOpponents = effect.onlyOpponents;
}
@Override
public SacrificeAllEffect copy() {
return new SacrificeAllEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
int num = amount.calculate(game, source, this);
if (num < 1) {
return false;
}
Set<UUID> perms = new HashSet<>();
for (UUID playerId : onlyOpponents ?
game.getOpponents(source.getControllerId()) :
game.getState().getPlayersInRange(source.getControllerId(), game)) {
Player player = game.getPlayer(playerId);
if (player == null) {
continue;
}
int numTargets = Math.min(num, game.getBattlefield().count(TargetSacrifice.makeFilter(filter), player.getId(), source, game));
if (numTargets < 1) {
continue;
}
TargetSacrifice target = new TargetSacrifice(numTargets, filter);
target.choose(Outcome.Sacrifice, player.getId(), source, game);
perms.addAll(target.getTargets());
}
List<Permanent> sacraficedPermanents = new ArrayList<>();
for (UUID permID : perms) {
Permanent permanent = game.getPermanent(permID);
if (permanent != null && permanent.sacrifice(source, game)) {
sacraficedPermanents.add(permanent.copy());
}
}
saveSacrificedPermanentsList(source.getSourceId(), game, sacraficedPermanents);
return true;
}
public static void saveSacrificedPermanentsList(UUID sourceObjectId, Game game, List<Permanent> list) {
game.getState().setValue(CardUtil.getCardZoneString(VALUE_KEY, sourceObjectId, game), list);
}
/**
* Get detailed list of sacrificed permanents
*
* @param previous if you need to look in detailed list on battlefield, then use previous param to find data from a stack moment
*/
public static List<Permanent> getSacrificedPermanentsList(UUID sourceObjectId, Game game, boolean previous) {
return (List<Permanent>) game.getState().getValue(CardUtil.getCardZoneString(VALUE_KEY, sourceObjectId, game, previous));
}
private void setText() {
StringBuilder sb = new StringBuilder();
sb.append(onlyOpponents ? "each opponent sacrifices " : "each player sacrifices ");
switch (amount.toString()) {
case "X":
sb.append(amount.toString());
sb.append(' ');
sb.append(filter.getMessage());
break;
case "1":
sb.append(CardUtil.addArticle(filter.getMessage()));
break;
default:
sb.append(CardUtil.numberToText(amount.toString(), "a"));
sb.append(' ');
sb.append(filter.getMessage());
}
if (!filter.getMessage().contains("with")) {
sb.append(" of their choice");
}
staticText = sb.toString();
}
}