Merge pull request #5394 from Zzooouhh/Zzooouhh-bandswithother

Implemented Bands With Other & related cards
This commit is contained in:
L_J 2018-10-21 22:33:33 +02:00 committed by GitHub
commit 4e231f51a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 727 additions and 43 deletions

View file

@ -0,0 +1,79 @@
package mage.abilities.keyword;
import mage.abilities.StaticAbility;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
/**
*
* @author L_J
*/
public class BandsWithOtherAbility extends StaticAbility {
private SubType subtype;
private SuperType supertype;
private String bandingName;
public BandsWithOtherAbility() {
this(null, null, null);
}
public BandsWithOtherAbility(SubType subtype) {
this(subtype, null, null);
}
public BandsWithOtherAbility(SuperType supertype) {
this(null, supertype, null);
}
public BandsWithOtherAbility(String bandingName) {
this(null, null, bandingName);
}
public BandsWithOtherAbility(SubType subtype, SuperType supertype, String bandingName) {
super(Zone.ALL, null);
this.subtype = subtype;
this.supertype = supertype;
this.bandingName = bandingName;
}
public BandsWithOtherAbility(BandsWithOtherAbility ability) {
super(ability);
this.subtype = ability.subtype;
this.supertype = ability.supertype;
this.bandingName = ability.bandingName;
}
@Override
public BandsWithOtherAbility copy() {
return new BandsWithOtherAbility(this);
}
public SubType getSubtype() {
return subtype;
}
public SuperType getSupertype() {
return supertype;
}
public String getName() {
return bandingName;
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder("bands with other");
if (subtype != null) {
return sb.append(' ').append(subtype.getDescription()).append('s').toString();
} else if (supertype != null) {
return sb.append(' ').append(supertype.toString()).append(" creatures").toString();
} else if (bandingName != null) {
return sb.append(" creatures named ").append(bandingName).toString();
}
return "all \"" + sb.toString() + "\" abilities";
}
}

View file

@ -6,6 +6,7 @@ import mage.abilities.Ability;
import mage.abilities.effects.RequirementEffect;
import mage.abilities.effects.RestrictionEffect;
import mage.abilities.keyword.BandingAbility;
import mage.abilities.keyword.BandsWithOtherAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.abilities.keyword.special.JohanVigilanceAbility;
import mage.constants.Outcome;
@ -14,8 +15,12 @@ import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterCreatureForCombatBlock;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.mageobject.SupertypePredicate;
import mage.filter.predicate.permanent.AttackingSameNotBandedPredicate;
import mage.filter.predicate.permanent.PermanentIdPredicate;
import mage.game.Game;
@ -293,53 +298,115 @@ public class Combat implements Serializable, Copyable<Combat> {
Permanent attacker = game.getPermanent(creatureId);
if (attacker != null && player != null) {
CombatGroup combatGroup = findGroup(attacker.getId());
if (combatGroup != null && attacker.getAbilities().containsKey(BandingAbility.getInstance().getId()) && attacker.getBandedCards().isEmpty() && getAttackers().size() > 1) {
boolean isBanded = false;
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("attacking creature to band with " + attacker.getLogName());
filter.add(Predicates.not(new PermanentIdPredicate(creatureId)));
filter.add(new AttackingSameNotBandedPredicate(combatGroup.getDefenderId())); // creature that isn't already banded, and is attacking the same player or planeswalker
while (player.canRespond()) {
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true);
target.setRequired(false);
if (!target.canChoose(attackingPlayerId, game)
|| game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackingPlayerId, attackingPlayerId))
|| !player.chooseUse(Outcome.Benefit, "Do you wish to " + (isBanded ? "band " + attacker.getLogName() + " with another " : "form a band with " + attacker.getLogName() + " and an ") + "attacking creature?", null, game)) {
break;
}
if (target.choose(Outcome.Benefit, attackingPlayerId, null, game)) {
isBanded = true;
for (UUID targetId : target.getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent != null) {
for (UUID bandedId : attacker.getBandedCards()) {
permanent.addBandedCard(bandedId);
Permanent banded = game.getPermanent(bandedId);
if (banded != null) {
banded.addBandedCard(targetId);
}
}
permanent.addBandedCard(creatureId);
attacker.addBandedCard(targetId);
if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
filter.add(new AbilityPredicate(BandingAbility.class));
}
}
}
if (combatGroup != null && attacker.getBandedCards().isEmpty() && getAttackers().size() > 1) {
boolean canBand = attacker.getAbilities().containsKey(BandingAbility.getInstance().getId());
List<Ability> bandsWithOther = new ArrayList<>();
for (Ability ability : attacker.getAbilities()) {
if (ability.getClass().equals(BandsWithOtherAbility.class)) {
bandsWithOther.add(ability);
}
}
if (isBanded) {
StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ").append((attacker.getBandedCards().size() + 1) + " creatures: ");
sb.append(attacker.getLogName());
for (UUID id : attacker.getBandedCards()) {
sb.append(", ");
Permanent permanent = game.getPermanent(id);
if (permanent != null) {
sb.append(permanent.getLogName());
boolean canBandWithOther = !bandsWithOther.isEmpty();
if (canBand || canBandWithOther) {
boolean isBanded = false;
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("attacking creature to band with " + attacker.getLogName());
filter.add(Predicates.not(new PermanentIdPredicate(creatureId)));
filter.add(new AttackingSameNotBandedPredicate(combatGroup.getDefenderId())); // creature that isn't already banded, and is attacking the same player or planeswalker
List<Predicate<MageObject>> predicates = new ArrayList<>();
if (!canBand && canBandWithOther) {
for (Ability ab : bandsWithOther) {
BandsWithOtherAbility ability = (BandsWithOtherAbility) ab;
if (ability.getSubtype() != null) {
predicates.add(new SubtypePredicate(ability.getSubtype()));
}
if (ability.getSupertype() != null) {
predicates.add(new SupertypePredicate(ability.getSupertype()));
}
if (ability.getName() != null) {
predicates.add(new NamePredicate(ability.getName()));
}
}
filter.add(Predicates.or(predicates));
}
while (player.canRespond()) {
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true);
target.setRequired(false);
canBand &= target.canChoose(attackingPlayerId, game);
canBandWithOther &= target.canChoose(attackingPlayerId, game);
if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackingPlayerId, attackingPlayerId))
|| (!canBand && !canBandWithOther)
|| !player.chooseUse(Outcome.Benefit, "Do you wish to " + (isBanded ? "band " + attacker.getLogName() + " with another " : "form a band with " + attacker.getLogName() + " and an ") + "attacking creature?", null, game)) {
break;
}
if (canBand && canBandWithOther) {
if (player.chooseUse(Outcome.Detriment, "Choose type of banding ability to apply:", attacker.getLogName(), "Banding", "Bands with other", null, game)) {
canBandWithOther = false;
} else {
canBand = false;
for (Ability ab : bandsWithOther) {
BandsWithOtherAbility ability = (BandsWithOtherAbility) ab;
if (ability.getSubtype() != null) {
predicates.add(new SubtypePredicate(ability.getSubtype()));
}
if (ability.getSupertype() != null) {
predicates.add(new SupertypePredicate(ability.getSupertype()));
}
if (ability.getName() != null) {
predicates.add(new NamePredicate(ability.getName()));
}
}
filter.add(Predicates.or(predicates));
}
}
if (target.choose(Outcome.Benefit, attackingPlayerId, null, game)) {
isBanded = true;
for (UUID targetId : target.getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent != null) {
for (UUID bandedId : attacker.getBandedCards()) {
permanent.addBandedCard(bandedId);
Permanent banded = game.getPermanent(bandedId);
if (banded != null) {
banded.addBandedCard(targetId);
}
}
permanent.addBandedCard(creatureId);
attacker.addBandedCard(targetId);
if (canBand) {
if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
filter.add(new AbilityPredicate(BandingAbility.class));
canBandWithOther = false;
}
} else if (canBandWithOther) {
List<Predicate<MageObject>> newPredicates = new ArrayList<>();
for (Predicate<MageObject> predicate : predicates) {
if (predicate.apply(permanent, game)) {
newPredicates.add(predicate);
}
}
filter.add(Predicates.or(newPredicates));
canBand = false;
}
}
}
}
}
game.informPlayers(sb.toString());
if (isBanded) {
StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ").append((attacker.getBandedCards().size() + 1) + " creatures: ");
sb.append(attacker.getLogName());
for (UUID id : attacker.getBandedCards()) {
sb.append(", ");
Permanent permanent = game.getPermanent(id);
if (permanent != null) {
sb.append(permanent.getLogName());
}
}
game.informPlayers(sb.toString());
}
}
}
}

View file

@ -5,10 +5,12 @@ import java.io.Serializable;
import java.util.*;
import java.util.stream.Stream;
import mage.abilities.Ability;
import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility;
import mage.abilities.common.ControllerDivideCombatDamageAbility;
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
import mage.abilities.keyword.BandingAbility;
import mage.abilities.keyword.BandsWithOtherAbility;
import mage.abilities.keyword.CantBlockAloneAbility;
import mage.abilities.keyword.DeathtouchAbility;
import mage.abilities.keyword.DoubleStrikeAbility;
@ -110,6 +112,56 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
return perm.getAbilities().containsKey(BandingAbility.getInstance().getId());
}
private boolean appliesBandsWithOther(List<UUID> creatureIds, Game game) {
for (UUID creatureId : creatureIds) {
Permanent perm = game.getPermanent(creatureId);
if (perm != null && perm.getBandedCards() != null) {
for (Ability ab : perm.getAbilities()) {
if (ab.getClass().equals(BandsWithOtherAbility.class)) {
BandsWithOtherAbility ability = (BandsWithOtherAbility) ab;
if (ability.getSubtype() != null) {
if (perm.hasSubtype(ability.getSubtype(), game)) {
for (UUID bandedId : creatureIds) {
if (!bandedId.equals(creatureId)) {
Permanent banded = game.getPermanent(bandedId);
if (banded != null && banded.hasSubtype(ability.getSubtype(), game)) {
return true;
}
}
}
}
}
if (ability.getSupertype() != null) {
if (perm.getSuperType().contains(ability.getSupertype())) {
for (UUID bandedId : creatureIds) {
if (!bandedId.equals(creatureId)) {
Permanent banded = game.getPermanent(bandedId);
if (banded != null && banded.getSuperType().contains(ability.getSupertype())) {
return true;
}
}
}
}
}
if (ability.getName() != null) {
if (perm.getName().equals(ability.getName())) {
for (UUID bandedId : creatureIds) {
if (!bandedId.equals(creatureId)) {
Permanent banded = game.getPermanent(bandedId);
if (banded != null && banded.getName().equals(ability.getName())) {
return true;
}
}
}
}
}
}
}
}
}
return false;
}
public void assignDamageToBlockers(boolean first, Game game) {
if (!attackers.isEmpty() && (!first || hasFirstOrDoubleStrike(game))) {
Permanent attacker = game.getPermanent(attackers.get(0));
@ -837,6 +889,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
}
}
if (appliesBandsWithOther(attackers, game)) { // 702.21k - both a [quality] creature with bands with other [quality] and another [quality] creature (...)
return true;
}
return false;
}
@ -855,6 +910,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
}
}
if (appliesBandsWithOther(blockers, game)) { // 702.21j - both a [quality] creature with bands with other [quality] and another [quality] creature (...)
return true;
}
for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) {
if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) {
return true;

View file

@ -0,0 +1,32 @@
package mage.game.permanent.token;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.MageInt;
import mage.abilities.keyword.BandsWithOtherAbility;
/**
*
* @author L_J
*/
public final class WolvesOfTheHuntToken extends TokenImpl {
public WolvesOfTheHuntToken() {
super("Wolves of the Hunt", "1/1 green Wolf creature token named Wolves of the Hunt");
cardType.add(CardType.CREATURE);
subtype.add(SubType.WOLF);
color.setGreen(true);
power = new MageInt(1);
toughness = new MageInt(1);
this.addAbility(new BandsWithOtherAbility("Wolves of the Hunt"));
}
public WolvesOfTheHuntToken(final WolvesOfTheHuntToken token) {
super(token);
}
public WolvesOfTheHuntToken copy() {
return new WolvesOfTheHuntToken(this);
}
}