1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-04-02 03:18:09 -09:00

Added logic to limit number of allowed attackers attacking defending player.

This commit is contained in:
LevelX2 2013-11-30 23:26:45 +01:00
parent b6275c1bae
commit b1250353ee
6 changed files with 207 additions and 59 deletions
Mage/src/mage

View file

@ -33,6 +33,7 @@ import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.target.targetpointer.FixedTarget;
/**
*
@ -40,13 +41,20 @@ import mage.game.events.GameEvent;
*/
public class GainLifeControllerTriggeredAbility extends TriggeredAbilityImpl<GainLifeControllerTriggeredAbility> {
private boolean setTargetPointer;
public GainLifeControllerTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, false);
}
public GainLifeControllerTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) {
super(Zone.BATTLEFIELD, effect, optional);
this.setTargetPointer = setTargetPointer;
}
public GainLifeControllerTriggeredAbility(final GainLifeControllerTriggeredAbility ability) {
super(ability);
this.setTargetPointer = ability.setTargetPointer;
}
@Override
@ -56,7 +64,16 @@ public class GainLifeControllerTriggeredAbility extends TriggeredAbilityImpl<Gai
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.GAINED_LIFE && event.getPlayerId().equals(this.getControllerId());
if (event.getType() == GameEvent.EventType.GAINED_LIFE && event.getPlayerId().equals(this.getControllerId())) {
if (setTargetPointer) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getPlayerId()));
effect.setValue("gainedLife", event.getAmount());
}
return true;
}
}
return false;
}
@Override

View file

@ -45,22 +45,41 @@ import mage.game.permanent.Permanent;
*/
public class GainControlTargetEffect extends ContinuousEffectImpl<GainControlTargetEffect> {
private UUID fixedControllerId;
private boolean fixedController;
private UUID controllingPlayerId;
private boolean fixedControl;
public GainControlTargetEffect(Duration duration) {
this(duration, false);
this(duration, false, null);
}
public GainControlTargetEffect(Duration duration, boolean fixedController) {
/**
*
* @param duration
* @param fixedControl Controlling player is fixed even if the controller of the ability chnages
*/
public GainControlTargetEffect(Duration duration, boolean fixedControl) {
this(duration, fixedControl, null);
}
/**
*
* @param duration
* @param controllingPlayerId Player that controlls the target creature
*/
public GainControlTargetEffect(Duration duration, UUID controllingPlayerId) {
this(duration, true, controllingPlayerId);
}
public GainControlTargetEffect(Duration duration, boolean fixedControl, UUID controllingPlayerId) {
super(duration, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl);
this.fixedController = fixedController;
this.controllingPlayerId = controllingPlayerId;
this.fixedControl = fixedControl;
}
public GainControlTargetEffect(final GainControlTargetEffect effect) {
super(effect);
this.fixedControllerId = effect.fixedControllerId;
this.fixedController = effect.fixedController;
this.controllingPlayerId = effect.controllingPlayerId;
this.fixedControl = effect.fixedControl;
}
@Override
@ -70,8 +89,8 @@ public class GainControlTargetEffect extends ContinuousEffectImpl<GainControlTar
@Override
public void init(Ability source, Game game) {
if (fixedController) {
this.fixedControllerId = source.getControllerId();
if (this.controllingPlayerId == null && fixedControl) {
this.controllingPlayerId = source.getControllerId();
}
}
@ -79,18 +98,21 @@ public class GainControlTargetEffect extends ContinuousEffectImpl<GainControlTar
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source));
if (permanent != null) {
if (fixedController) {
return permanent.changeControllerId(fixedControllerId, game);
if (controllingPlayerId != null) {
return permanent.changeControllerId(controllingPlayerId, game);
} else {
return permanent.changeControllerId(source.getControllerId(), game);
}
}
this.discard();
return false;
}
@Override
public String getText(Mode mode) {
if (!staticText.isEmpty()) {
return staticText;
}
return "Gain control of target " + mode.getTargets().get(0).getTargetName() + " " + duration.toString();
}
}

View file

@ -53,6 +53,7 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.players.PlayerList;
import mage.target.common.TargetDefender;
import mage.util.CardUtil;
import mage.util.Copyable;
import mage.util.trace.TraceUtil;
@ -70,10 +71,15 @@ public class Combat implements Serializable, Copyable<Combat> {
protected List<CombatGroup> groups = new ArrayList<CombatGroup>();
protected Map<UUID, CombatGroup> blockingGroups = new HashMap<UUID, CombatGroup>();
protected Set<UUID> defenders = new HashSet<UUID>();
// how many creatures attack defending player
protected Map<UUID, Set<UUID>> numberCreaturesDefenderAttackedBy = new HashMap<UUID, Set<UUID>>();
protected UUID attackerId; //the player that is attacking
// <creature that can block, <all attackers that force the creature to block it>>
protected Map<UUID, Set<UUID>> creaturesForcedToBlockAttackers = new HashMap<UUID, Set<UUID>>();
// which creature is forced to attack which defender(s). If set is empty, the creature can attack every possible defender
private final Map<UUID, Set<UUID>> creaturesForcedToAttack = new HashMap<UUID, Set<UUID>>();
private int maxAttackers = Integer.MIN_VALUE;
public Combat() {
@ -138,6 +144,9 @@ public class Combat implements Serializable, Copyable<Combat> {
defenders.clear();
attackerId = null;
creaturesForcedToBlockAttackers.clear();
numberCreaturesDefenderAttackedBy.clear();
creaturesForcedToAttack.clear();
maxAttackers = Integer.MIN_VALUE;
}
public String getValue() {
@ -207,27 +216,38 @@ public class Combat implements Serializable, Copyable<Combat> {
protected void checkAttackRequirements(Player player, Game game) {
//20101001 - 508.1d
for (Permanent creature : player.getAvailableAttackers(game)) {
boolean mustAttack = false;
Set <UUID> defendersForcedToAttack = new HashSet<UUID>();
for (Map.Entry entry : game.getContinuousEffects().getApplicableRequirementEffects(creature, game).entrySet()) {
RequirementEffect effect = (RequirementEffect)entry.getKey();
if (effect.mustAttack(game)) {
mustAttack = true;
for (Ability ability: (HashSet<Ability>)entry.getValue()) {
UUID defenderId = effect.mustAttackDefender(ability, game);
if (defenderId == null) {
if (defenders.size() == 1) {
player.declareAttacker(creature.getId(), defenders.iterator().next(), game);
} else {
TargetDefender target = new TargetDefender(defenders, creature.getId());
target.setRequired(true);
if (player.chooseTarget(Outcome.Damage, target, null, game)) {
player.declareAttacker(creature.getId(), target.getFirstTarget(), game);
}
}
} else {
player.declareAttacker(creature.getId(), defenderId, game);
if(defenderId != null) {
defendersForcedToAttack.add(defenderId);
}
break;
}
}
}
if (mustAttack) {
creaturesForcedToAttack.put(creature.getId(),defendersForcedToAttack);
if (defendersForcedToAttack.isEmpty()) {
if (defenders.size() == 1) {
player.declareAttacker(creature.getId(), defenders.iterator().next(), game);
} else {
TargetDefender target = new TargetDefender(defenders, creature.getId());
target.setRequired(true);
if (player.chooseTarget(Outcome.Damage, target, null, game)) {
player.declareAttacker(creature.getId(), target.getFirstTarget(), game);
}
}
} else {
player.declareAttacker(creature.getId(), defendersForcedToAttack.iterator().next(), game);
}
}
}
}
@ -300,35 +320,42 @@ public class Combat implements Serializable, Copyable<Combat> {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, defenderId, defenderId));
// add info about attacker blocked by blocker to the game log
boolean shownDefendingPlayer = false;
for (CombatGroup group :this.getGroups()) {
StringBuilder sb = new StringBuilder();
for(UUID attackingCreatureId : group.getAttackers()) {
Permanent attackingCreature = game.getPermanent(attackingCreatureId);
if (attackingCreature != null) {
sb.append(attackingCreature.getName()).append(" (");
sb.append(attackingCreature.getPower().getValue()).append("/").append(attackingCreature.getToughness().getValue()).append(") ");
} else {
// creature left battlefield
attackingCreature = (Permanent) game.getLastKnownInformation(attackingCreatureId, Zone.BATTLEFIELD);
if (group.defendingPlayerId.equals(defenderId)) {
if (!shownDefendingPlayer && defender != null) {
game.informPlayers(new StringBuilder("Attacked player: ").append(defender.getName()).toString());
shownDefendingPlayer = true;
}
StringBuilder sb = new StringBuilder();
for(UUID attackingCreatureId : group.getAttackers()) {
Permanent attackingCreature = game.getPermanent(attackingCreatureId);
if (attackingCreature != null) {
sb.append(attackingCreature.getName()).append(" [left battlefield)] ");
sb.append(attackingCreature.getName()).append(" (");
sb.append(attackingCreature.getPower().getValue()).append("/").append(attackingCreature.getToughness().getValue()).append(") ");
} else {
// creature left battlefield
attackingCreature = (Permanent) game.getLastKnownInformation(attackingCreatureId, Zone.BATTLEFIELD);
if (attackingCreature != null) {
sb.append(attackingCreature.getName()).append(" [left battlefield)] ");
}
}
}
}
if (group.getBlockers().size() > 0) {
sb.append("blocked by ");
for(UUID blockingCreatureId : group.getBlockers()) {
Permanent blockingCreature = game.getPermanent(blockingCreatureId);
if (blockingCreature != null) {
sb.append(blockingCreature.getName()).append(" (");
sb.append(blockingCreature.getPower().getValue()).append("/").append(blockingCreature.getToughness().getValue()).append(") ");
if (group.getBlockers().size() > 0) {
sb.append("blocked by ");
for(UUID blockingCreatureId : group.getBlockers()) {
Permanent blockingCreature = game.getPermanent(blockingCreatureId);
if (blockingCreature != null) {
sb.append(blockingCreature.getName()).append(" (");
sb.append(blockingCreature.getPower().getValue()).append("/").append(blockingCreature.getToughness().getValue()).append(") ");
}
}
}
} else{
sb.append("unblocked");
} else{
sb.append("unblocked");
}
game.informPlayers(sb.toString());
}
game.informPlayers(sb.toString());
}
}
@ -647,17 +674,38 @@ public class Combat implements Serializable, Copyable<Combat> {
}
private void addDefender(UUID defenderId, Game game) {
defenders.add(defenderId);
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPlaneswalker, defenderId, game)) {
defenders.add(permanent.getId());
if (!defenders.contains(defenderId)) {
if (maxAttackers < Integer.MAX_VALUE) {
Player defendingPlayer = game.getPlayer(defenderId);
if (defendingPlayer != null) {
if (defendingPlayer.getMaxAttackedBy() == Integer.MAX_VALUE) {
maxAttackers = Integer.MAX_VALUE;
} else {
if (maxAttackers == Integer.MIN_VALUE) {
maxAttackers = defendingPlayer.getMaxAttackedBy();
} else {
maxAttackers += defendingPlayer.getMaxAttackedBy();
}
}
}
}
defenders.add(defenderId);
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPlaneswalker, defenderId, game)) {
defenders.add(permanent.getId());
}
}
}
public void declareAttacker(UUID attackerId, UUID defenderId, Game game) {
public boolean declareAttacker(UUID attackerId, UUID defenderId, Game game) {
if (!defenders.contains(defenderId)) {
return;
return false;
}
Permanent defender = game.getPermanent(defenderId);
// Check if defending player can be attacked with another creature
if (!canDefenderBeAttacked(attackerId, defenderId, game)) {
return false;
}
CombatGroup newGroup = new CombatGroup(defenderId, defender != null, defender != null ? defender.getControllerId(): defenderId);
newGroup.attackers.add(attackerId);
Permanent attacker = game.getPermanent(attackerId);
@ -666,6 +714,39 @@ public class Combat implements Serializable, Copyable<Combat> {
}
attacker.setAttacking(true);
groups.add(newGroup);
return true;
}
public boolean canDefenderBeAttacked(UUID attackerId, UUID defenderId, Game game) {
Permanent defender = game.getPermanent(defenderId);
// Check if defending player can be attacked with another creature
if (defender != null) {
// a planeswalker is attacked, there exits no restriction yet for attacking planeswalker
return true;
}
Player defendingPlayer = game.getPlayer(defenderId);
if (defendingPlayer == null) {
return false;
}
Set<UUID> defenderAttackedBy;
if (numberCreaturesDefenderAttackedBy.containsKey(defendingPlayer.getId())) {
defenderAttackedBy = numberCreaturesDefenderAttackedBy.get(defendingPlayer.getId());
} else {
defenderAttackedBy = new HashSet<UUID>();
numberCreaturesDefenderAttackedBy.put(defendingPlayer.getId(), defenderAttackedBy);
}
if (defenderAttackedBy.size() >= defendingPlayer.getMaxAttackedBy()) {
Player attackingPlayer = game.getPlayer(game.getControllerId(attackerId));
if (attackingPlayer != null) {
game.informPlayer(attackingPlayer, new StringBuilder("No more than ")
.append(CardUtil.numberToText(defendingPlayer.getMaxAttackedBy()))
.append(" creatures can attack ")
.append(defendingPlayer.getName()).toString());
}
return false;
}
defenderAttackedBy.add(attackerId);
return true;
}
// add blocking group for creatures that block more than one creature
@ -757,10 +838,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
public boolean noAttackers() {
if (groups.isEmpty() || getAttackers().isEmpty()) {
return true;
}
return false;
return groups.isEmpty() || getAttackers().isEmpty();
}
public boolean isAttacked(UUID defenderId, Game game) {
@ -845,6 +923,9 @@ public class Combat implements Serializable, Copyable<Combat> {
if (group.attackers.contains(attackerId)) {
group.attackers.remove(attackerId);
group.attackerOrder.remove(attackerId);
for (Set<UUID> attackingCreatures : numberCreaturesDefenderAttackedBy.values()) {
attackingCreatures.remove(attackerId);
}
Permanent creature = game.getPermanent(attackerId);
if (creature != null) {
creature.setAttacking(false);
@ -878,6 +959,15 @@ public class Combat implements Serializable, Copyable<Combat> {
return attackerId;
}
public Map<UUID, Set<UUID>> getCreaturesForcedToAttack() {
return creaturesForcedToAttack;
}
public int getMaxAttackers() {
return maxAttackers;
}
@Override
public Combat copy() {
return new Combat(this);

View file

@ -102,6 +102,8 @@ public interface Player extends MageItem, Copyable<Player> {
void setLandsPerTurn(int landsPerTurn);
int getMaxHandSize();
void setMaxHandSize(int maxHandSize);
int getMaxAttackedBy();
void setMaxAttackedBy(int maxAttackedBy);
boolean isPassed();
boolean isEmptyDraw();
void pass(Game game);

View file

@ -139,6 +139,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
protected int landsPlayed;
protected int landsPerTurn = 1;
protected int maxHandSize = 7;
protected int maxAttackedBy = Integer.MAX_VALUE;
protected ManaPool manaPool;
protected boolean passed;
protected boolean passedTurn;
@ -219,6 +220,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
this.landsPlayed = player.landsPlayed;
this.landsPerTurn = player.landsPerTurn;
this.maxHandSize = player.maxHandSize;
this.maxAttackedBy = player.maxAttackedBy;
this.manaPool = player.manaPool.copy();
this.turns = player.turns;
@ -269,6 +271,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
this.landsPlayed = player.getLandsPlayed();
this.landsPerTurn = player.getLandsPerTurn();
this.maxHandSize = player.getMaxHandSize();
this.maxAttackedBy = player.getMaxAttackedBy();
this.manaPool = player.getManaPool().copy();
this.turns = player.getTurns();
@ -345,6 +348,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
this.abilities.clear();
this.landsPerTurn = 1;
this.maxHandSize = 7;
this.maxAttackedBy = Integer.MAX_VALUE;
this.canGainLife = true;
this.canLoseLife = true;
this.canPayLifeCost = true;
@ -1386,6 +1390,16 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
this.maxHandSize = maxHandSize;
}
@Override
public void setMaxAttackedBy(int maxAttackedBy) {
this.maxAttackedBy = maxAttackedBy;
}
@Override
public int getMaxAttackedBy() {
return maxAttackedBy;
}
@Override
public void setResponseString(String responseString) {}
@ -1604,6 +1618,8 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
}
/**
* @param game
* @param appliedEffects
*
* @return true if player won the toss
*/

View file

@ -50,9 +50,10 @@ public interface Target extends Serializable {
void clearChosen();
boolean isNotTarget();
// controlls if it will be checked, if the target can be targeted from source
// true = check for protection
// false = do not check for protection
/**
* controlls if it will be checked, if the target can be targeted from source
* @param notTarget true = check for protection, false = do not check for protection
*/
void setNotTarget(boolean notTarget);
// methods for targets