mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 04:52:07 -08:00
Ability refactor: new code to search abilities in cards and permanents;
This commit is contained in:
parent
978118148b
commit
8af43dc13a
31 changed files with 85 additions and 47 deletions
|
|
@ -41,9 +41,13 @@ public interface MageObject extends MageItem, Serializable {
|
|||
|
||||
Set<SuperType> getSuperType();
|
||||
|
||||
/**
|
||||
* For cards: return basic abilities (without dynamic added)
|
||||
* For permanents: return all abilities (dynamic ability inserts into permanent)
|
||||
*/
|
||||
Abilities<Ability> getAbilities();
|
||||
|
||||
boolean hasAbility(UUID abilityId, Game game);
|
||||
boolean hasAbility(Ability ability, Game game);
|
||||
|
||||
ObjectColor getColor(Game game);
|
||||
|
||||
|
|
@ -180,9 +184,9 @@ public interface MageObject extends MageItem, Serializable {
|
|||
}
|
||||
|
||||
if (this.isCreature() && otherCard.isCreature()) {
|
||||
if (this.getAbilities().contains(ChangelingAbility.getInstance())
|
||||
if (this.hasAbility(ChangelingAbility.getInstance(), game)
|
||||
|| this.isAllCreatureTypes()
|
||||
|| otherCard.getAbilities().contains(ChangelingAbility.getInstance())
|
||||
|| otherCard.hasAbility(ChangelingAbility.getInstance(), game)
|
||||
|| otherCard.isAllCreatureTypes()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,12 +132,12 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
if (this.getAbilities().containsKey(abilityId)) {
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
if (this.getAbilities().contains(ability)) {
|
||||
return true;
|
||||
}
|
||||
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(getId());
|
||||
return otherAbilities != null && otherAbilities.containsKey(abilityId);
|
||||
return otherAbilities != null && otherAbilities.contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
}
|
||||
return null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
|
||||
|| timing == TimingRule.INSTANT
|
||||
|| object.hasAbility(FlashAbility.getInstance().getId(), game)
|
||||
|| object.hasAbility(FlashAbility.getInstance(), game)
|
||||
|| game.canPlaySorcery(playerId);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ public enum SuspendedCondition implements Condition {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof SuspendAbility);
|
||||
boolean found = card.getAbilities(game).containsClass(SuspendAbility.class);
|
||||
|
||||
if (!found) {
|
||||
found = game.getState().getAllOtherAbilities(source.getSourceId()).stream().anyMatch(ability -> ability instanceof SuspendAbility);
|
||||
found = game.getState().getAllOtherAbilities(source.getSourceId()).containsClass(SuspendAbility.class);
|
||||
|
||||
}
|
||||
if (found) {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ public class GainAbilitySpellsEffect extends ContinuousEffectImpl {
|
|||
if (stackObject.isControlledBy(source.getControllerId())) {
|
||||
Card card = game.getCard(stackObject.getSourceId());
|
||||
if (card != null && filter.match(card, game)) {
|
||||
if (!card.getAbilities().contains(ability)) {
|
||||
if (!card.hasAbility(ability, game)) {
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class CanBlockOnlyFlyingAttachedEffect extends RestrictionEffect {
|
|||
if (attacker == null) {
|
||||
return true;
|
||||
}
|
||||
return attacker.getAbilities().contains(FlyingAbility.getInstance());
|
||||
return attacker.hasAbility(FlyingAbility.getInstance(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class CanBlockOnlyFlyingEffect extends RestrictionEffect {
|
|||
if (attacker == null) {
|
||||
return true;
|
||||
}
|
||||
return attacker.getAbilities().contains(FlyingAbility.getInstance());
|
||||
return attacker.hasAbility(FlyingAbility.getInstance(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
|
|||
if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.isControlledBy(source.getControllerId())) {
|
||||
Spell spell = (Spell) stackObject;
|
||||
if (filter.match(spell, game)) {
|
||||
if (!spell.getAbilities().contains(ability)) {
|
||||
if (!spell.hasAbility(ability, game)) {
|
||||
game.getState().addOtherAbility(spell.getCard(), ability);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ public class SuspendAbility extends SpecialAction {
|
|||
}
|
||||
MageObject object = game.getObject(sourceId);
|
||||
return new ActivationStatus(object.isInstant()
|
||||
|| object.hasAbility(FlashAbility.getInstance().getId(), game)
|
||||
|| object.hasAbility(FlashAbility.getInstance(), game)
|
||||
|| null != game.getContinuousEffects().asThough(sourceId,
|
||||
AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
|
||||
|| game.canPlaySorcery(playerId), null);
|
||||
|
|
@ -356,6 +356,13 @@ class SuspendPlayCardEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
// remove the abilities from the card
|
||||
// TODO: will not work with Adventure Cards and another auto-generated abilities list
|
||||
// TODO: is it work after blink or return to hand?
|
||||
/*
|
||||
bug example:
|
||||
Epochrasite bug: It comes out of suspend, is cast and enters the battlefield. THEN if it's returned to
|
||||
its owner's hand from battlefield, the bounced Epochrasite can't be cast for the rest of the game.
|
||||
*/
|
||||
card.getAbilities().removeAll(abilitiesToRemove);
|
||||
}
|
||||
// cast the card for free
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
|
||||
/**
|
||||
* Gets all current abilities - includes additional abilities added by other
|
||||
* cards or effects
|
||||
* cards or effects. Warning, you can't modify that list.
|
||||
*
|
||||
* @param game
|
||||
* @return A list of {@link Ability} - this collection is not modifiable
|
||||
|
|
@ -295,15 +295,23 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
if (game == null) {
|
||||
return abilities; // deck editor with empty game
|
||||
}
|
||||
|
||||
CardState cardState = game.getState().getCardState(this.getId());
|
||||
if (!cardState.hasLostAllAbilities() && (cardState.getAbilities() == null || cardState.getAbilities().isEmpty())) {
|
||||
if (cardState == null) {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
// collects all abilities
|
||||
Abilities<Ability> all = new AbilitiesImpl<>();
|
||||
|
||||
// basic
|
||||
if (!cardState.hasLostAllAbilities()) {
|
||||
all.addAll(abilities);
|
||||
}
|
||||
|
||||
// dynamic
|
||||
all.addAll(cardState.getAbilities());
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
|
|
@ -314,6 +322,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
cardState.getAbilities().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
// getAbilities(game) searches all abilities from base and dynamic lists (other)
|
||||
return this.getAbilities(game).contains(ability);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public in order to support adding abilities to SplitCardHalf's
|
||||
*
|
||||
|
|
|
|||
|
|
@ -171,8 +171,8 @@ public abstract class Designation implements MageObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
return abilites.containsKey(abilityId);
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return this.getAbilities().contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
|| getAttackers().size() <= 1) {
|
||||
return;
|
||||
}
|
||||
boolean canBand = attacker.getAbilities().containsKey(BandingAbility.getInstance().getId());
|
||||
boolean canBand = attacker.hasAbility(BandingAbility.getInstance(), game);
|
||||
List<Ability> bandsWithOther = new ArrayList<>();
|
||||
for (Ability ability : attacker.getAbilities()) {
|
||||
if (ability.getClass().equals(BandsWithOtherAbility.class)) {
|
||||
|
|
@ -390,7 +390,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
permanent.addBandedCard(creatureId);
|
||||
attacker.addBandedCard(targetId);
|
||||
if (canBand) {
|
||||
if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
|
||||
if (!permanent.hasAbility(BandingAbility.getInstance(), game)) {
|
||||
filter.add(new AbilityPredicate(BandingAbility.class));
|
||||
canBandWithOther = false;
|
||||
}
|
||||
|
|
@ -1289,7 +1289,8 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
if (attacker != null) {
|
||||
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, creatureId, playerId))) {
|
||||
if (addAttackerToCombat(creatureId, defenderId, game)) {
|
||||
if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId()) && !attacker.getAbilities().containsKey(JohanVigilanceAbility.getInstance().getId())) {
|
||||
if (!attacker.hasAbility(VigilanceAbility.getInstance(), game)
|
||||
&& !attacker.hasAbility(JohanVigilanceAbility.getInstance(), game)) {
|
||||
if (!attacker.isTapped()) {
|
||||
attacker.setTapped(true);
|
||||
attackersTappedByAttack.add(attacker.getId());
|
||||
|
|
|
|||
|
|
@ -158,12 +158,12 @@ public class Commander implements CommandObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
if (this.getAbilities().containsKey(abilityId)) {
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
if (this.getAbilities().contains(ability)) {
|
||||
return true;
|
||||
}
|
||||
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(getId());
|
||||
return otherAbilities != null && otherAbilities.containsKey(abilityId);
|
||||
return otherAbilities != null && otherAbilities.contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -173,8 +173,8 @@ public class Emblem implements CommandObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
return abilites.containsKey(abilityId);
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return getAbilities().contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -182,8 +182,8 @@ public class Plane implements CommandObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
return abilites.containsKey(abilityId);
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return getAbilities().contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ class TrailOfTheMageRingsReboundEffect extends ContinuousEffectImpl {
|
|||
|
||||
private void addReboundAbility(Card card, Ability source, Game game) {
|
||||
if (filter.match(card, game)) {
|
||||
boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof ReboundAbility);
|
||||
boolean found = card.getAbilities(game).containsClass(ReboundAbility.class);
|
||||
if (!found) {
|
||||
Ability ability = new ReboundAbility();
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
|
|
|
|||
|
|
@ -524,8 +524,8 @@ public class Spell extends StackObjImpl implements Card {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
return card.hasAbility(abilityId, game);
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return card.hasAbility(ability, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ public class StackAbility extends StackObjImpl implements Ability {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -672,4 +672,17 @@ public class StackAbility extends StackObjImpl implements Ability {
|
|||
public Outcome getCustomOutcome() {
|
||||
return this.ability.getCustomOutcome();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameInstance(Ability ability) {
|
||||
// same instance (by mtg rules) = same object, ID or class+text (you can't check class only cause it can be different by params/text)
|
||||
if (ability == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this == ability)
|
||||
|| (this.getId().equals(ability.getId()))
|
||||
|| (this.getOriginalId().equals(ability.getOriginalId()))
|
||||
|| (this.getClass() == ability.getClass() && this.getRule().equals(ability.getRule()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue