mirror of
https://github.com/magefree/mage.git
synced 2025-12-22 19:41:59 -08:00
Merge pull request #5394 from Zzooouhh/Zzooouhh-bandswithother
Implemented Bands With Other & related cards
This commit is contained in:
commit
4e231f51a1
14 changed files with 727 additions and 43 deletions
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue