mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Fix illegal distributions
This commit is contained in:
@@ -2149,7 +2149,7 @@ public class AiController {
|
|||||||
public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) {
|
public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) {
|
||||||
switch(sa.getApi())
|
switch(sa.getApi())
|
||||||
{
|
{
|
||||||
case SetLife:
|
case SetLife: // Reverse the Sands
|
||||||
if (relatedPlayer.equals(sa.getHostCard().getController())) {
|
if (relatedPlayer.equals(sa.getHostCard().getController())) {
|
||||||
return Collections.max(options);
|
return Collections.max(options);
|
||||||
} else if (relatedPlayer.isOpponentOf(sa.getHostCard().getController())) {
|
} else if (relatedPlayer.isOpponentOf(sa.getHostCard().getController())) {
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ package forge.game.ability.effects;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
@@ -18,31 +21,75 @@ public class LifeSetEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final boolean redistribute = sa.hasParam("Redistribute");
|
final boolean redistribute = sa.hasParam("Redistribute");
|
||||||
final int lifeAmount = redistribute ? 20 : AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("LifeAmount"), sa);
|
final int lifeAmount = redistribute ? 0 : AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("LifeAmount"), sa);
|
||||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||||
final List<Integer> lifetotals = new ArrayList<>();
|
final List<Integer> lifetotals = new ArrayList<>();
|
||||||
|
final PlayerCollection players = getTargetPlayers(sa);
|
||||||
|
|
||||||
if (redistribute) {
|
if (redistribute) {
|
||||||
for (final Player p : getTargetPlayers(sa)) {
|
for (final Player p : players) {
|
||||||
if (tgt == null || p.canBeTargetedBy(sa)) {
|
if (tgt == null || p.canBeTargetedBy(sa)) {
|
||||||
lifetotals.add(p.getLife());
|
lifetotals.add(p.getLife());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Player p : getTargetPlayers(sa)) {
|
for (final Player p : players.threadSafeIterable()) {
|
||||||
if (tgt == null || p.canBeTargetedBy(sa)) {
|
if (tgt == null || p.canBeTargetedBy(sa)) {
|
||||||
if (!redistribute) {
|
if (!redistribute) {
|
||||||
p.setLife(lifeAmount, sa.getHostCard());
|
p.setLife(lifeAmount, sa.getHostCard());
|
||||||
} else {
|
} else {
|
||||||
int life = sa.getActivatingPlayer().getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblLifeTotal") + ": " + p, lifetotals, p);
|
List<Integer> validChoices = getDistribution(players, true, lifetotals);
|
||||||
|
int life = sa.getActivatingPlayer().getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblLifeTotal") + ": " + p, validChoices, p);
|
||||||
p.setLife(life, sa.getHostCard());
|
p.setLife(life, sa.getHostCard());
|
||||||
lifetotals.remove((Integer) life);
|
lifetotals.remove((Integer) life);
|
||||||
|
players.remove(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<Integer> getDistribution(PlayerCollection players, boolean top, List<Integer> remainingChoices) {
|
||||||
|
// distribution was successful
|
||||||
|
if (players.isEmpty()) {
|
||||||
|
// carry signal back
|
||||||
|
remainingChoices.add(1);
|
||||||
|
return remainingChoices;
|
||||||
|
}
|
||||||
|
List<Integer> validChoices = Lists.newArrayList(remainingChoices);
|
||||||
|
for (Player p : players) {
|
||||||
|
for (Integer choice : remainingChoices) {
|
||||||
|
// 119.7/8 illegal choice
|
||||||
|
if ((p.getLife() < choice && !p.canGainLife()) || (p.getLife() > choice && !p.canLoseLife())) {
|
||||||
|
if (top) {
|
||||||
|
validChoices.remove(choice);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// combination is valid, check next
|
||||||
|
PlayerCollection nextPlayers = new PlayerCollection(players);
|
||||||
|
nextPlayers.remove(p);
|
||||||
|
List<Integer> nextChoices = new ArrayList<>(remainingChoices);
|
||||||
|
nextChoices.remove(choice);
|
||||||
|
nextChoices = getDistribution(nextPlayers, false, nextChoices);
|
||||||
|
if (nextChoices.isEmpty()) {
|
||||||
|
if (top) {
|
||||||
|
// top of recursion stack
|
||||||
|
validChoices.remove(choice);
|
||||||
|
}
|
||||||
|
} else if (!top) {
|
||||||
|
return nextChoices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (top) {
|
||||||
|
// checking first player is enough
|
||||||
|
return validChoices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.abilityfactory.AbilityFactoryAlterLife.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility)
|
* @see forge.card.abilityfactory.AbilityFactoryAlterLife.SpellEffect#getStackDescription(java.util.Map, forge.card.spellability.SpellAbility)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -483,15 +483,15 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return gainLife(lifeGain, source, null);
|
return gainLife(lifeGain, source, null);
|
||||||
}
|
}
|
||||||
public final boolean gainLife(int lifeGain, final Card source, final SpellAbility sa) {
|
public final boolean gainLife(int lifeGain, final Card source, final SpellAbility sa) {
|
||||||
|
if (!canGainLife()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Run any applicable replacement effects.
|
// Run any applicable replacement effects.
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
|
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
|
||||||
repParams.put(AbilityKey.LifeGained, lifeGain);
|
repParams.put(AbilityKey.LifeGained, lifeGain);
|
||||||
repParams.put(AbilityKey.Source, source);
|
repParams.put(AbilityKey.Source, source);
|
||||||
|
|
||||||
if (!canGainLife()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (getGame().getReplacementHandler().run(ReplacementType.GainLife, repParams)) {
|
switch (getGame().getReplacementHandler().run(ReplacementType.GainLife, repParams)) {
|
||||||
case NotReplaced:
|
case NotReplaced:
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user