* Added logic to add permitting object to play/cast events.

This commit is contained in:
LevelX2 2018-05-27 23:47:57 +02:00
parent b97a443a37
commit 27ced167fb
124 changed files with 1095 additions and 964 deletions

View file

@ -33,8 +33,10 @@ import java.util.*;
import java.util.Map.Entry;
import mage.ConditionalMana;
import mage.MageObject;
import mage.MageObjectReference;
import mage.Mana;
import mage.abilities.*;
import mage.abilities.ActivatedAbility.ActivationStatus;
import mage.abilities.common.PassAbility;
import mage.abilities.common.WhileSearchingPlayFromLibraryAbility;
import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility;
@ -610,7 +612,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
if (abilities.containsKey(HexproofAbility.getInstance().getId())) {
if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game)
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, this.getId(), game)) {
&& null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, this.getId(), game)) {
return false;
}
}
@ -1010,7 +1012,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming) {
public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference) {
if (card == null) {
return false;
}
@ -1018,7 +1020,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (card.isLand()) {
result = playLand(card, game, ignoreTiming);
} else {
result = cast(card.getSpellAbility(), game, noMana);
result = cast(card.getSpellAbility(), game, noMana, reference);
}
if (!result) {
game.informPlayer(this, "You can't play " + card.getIdName() + '.');
@ -1027,7 +1029,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean cast(SpellAbility ability, Game game, boolean noMana) {
public boolean cast(SpellAbility ability, Game game, boolean noMana, MageObjectReference permittingObject) {
if (game == null || ability == null) {
return false;
}
@ -1046,7 +1048,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
Card card = game.getCard(ability.getSourceId());
if (card != null) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getId(), ability.getSourceId(), playerId), ability)) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getId(), ability.getSourceId(), playerId, permittingObject), ability)) {
int bookmark = game.bookmarkState();
Zone fromZone = game.getState().getZone(card.getMainCard().getId());
card.cast(game, fromZone, ability, playerId);
@ -1074,10 +1076,10 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
setCastSourceIdWithAlternateMana(null, null, null);
GameEvent event = GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId);
GameEvent event = GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, permittingObject);
game.fireEvent(event);
if (spell.activate(game, noMana)) {
event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId);
event = GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, spell.getSpellAbility().getId(), spell.getSpellAbility().getSourceId(), playerId, permittingObject);
event.setZone(fromZone);
game.fireEvent(event);
if (!game.isSimulation()) {
@ -1116,7 +1118,7 @@ public abstract class PlayerImpl implements Player, Serializable {
SpellAbility spellAbility = new SpellAbility(null, "", game.getState().getZone(card.getId()), SpellAbilityType.FACE_DOWN_CREATURE);
spellAbility.setControllerId(this.getId());
spellAbility.setSourceId(card.getId());
if (cast(spellAbility, game, false)) {
if (cast(spellAbility, game, false, null)) {
return true;
}
}
@ -1124,17 +1126,18 @@ public abstract class PlayerImpl implements Player, Serializable {
return false;
}
//20091005 - 114.2a
if (!ignoreTiming && !playLandAbility.canActivate(this.playerId, game)) {
ActivationStatus activationStatus = playLandAbility.canActivate(this.playerId, game);
if (!ignoreTiming && !activationStatus.canActivate()) {
return false;
}
//20091005 - 305.1
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId))) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()))) {
// int bookmark = game.bookmarkState();
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) {
landsPlayed++;
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId, activationStatus.getPermittingObject()));
game.fireInformEvent(getLogName() + " plays " + card.getLogName());
// game.removeBookmark(bookmark);
resetStoredBookmark(game); // prevent undo after playing a land
@ -1239,7 +1242,8 @@ public abstract class PlayerImpl implements Player, Serializable {
Card card = game.getCard(ability.getSourceId());
result = playLand(card, game, false);
} else {
if (!ability.canActivate(this.playerId, game)) {
ActivationStatus activationStatus = ability.canActivate(this.playerId, game);
if (!activationStatus.canActivate()) {
return false;
}
@ -1251,7 +1255,7 @@ public abstract class PlayerImpl implements Player, Serializable {
result = playManaAbility((ActivatedManaAbilityImpl) ability.copy(), game);
break;
case SPELL:
result = cast((SpellAbility) ability.copy(), game, false);
result = cast((SpellAbility) ability.copy(), game, false, activationStatus.getPermittingObject());
break;
default:
result = playAbility(ability.copy(), game);
@ -1357,7 +1361,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (ability.getZone().match(zone)) {
if (ability instanceof ActivatedAbility) {
if (ability instanceof ActivatedManaAbilityImpl) {
if (((ActivatedAbility) ability).canActivate(playerId, game)) {
if (((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
output.put(ability.getId(), (ActivatedAbility) ability);
}
} else if (canPlay(((ActivatedAbility) ability), availableMana, object, game)) {
@ -1379,19 +1383,19 @@ public abstract class PlayerImpl implements Player, Serializable {
if (Zone.GRAVEYARD == zone && canPlayCardsFromGraveyard()) {
for (ActivatedAbility ability : candidateAbilites.getPlayableAbilities(Zone.HAND)) {
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.canActivate(playerId, game)) {
if (ability.canActivate(playerId, game).canActivate()) {
output.put(ability.getId(), ability);
}
}
}
}
if (zone != Zone.BATTLEFIELD
&& null != game.getContinuousEffects().asThough(object.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
&& null != game.getContinuousEffects().asThough(object.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
for (Ability ability : candidateAbilites) {
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
ability.setControllerId(this.getId());
if (ability instanceof ActivatedAbility && ability.getZone().match(Zone.HAND)
&& ((ActivatedAbility) ability).canActivate(playerId, game)) {
&& ((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
output.put(ability.getId(), (ActivatedAbility) ability);
}
}
@ -1439,7 +1443,7 @@ public abstract class PlayerImpl implements Player, Serializable {
flashbackAbility.setControllerId(card.getOwnerId());
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_LEFT);
flashbackAbility.setAbilityName(((SplitCard) card).getLeftHalfCard().getName());
if (flashbackAbility.canActivate(playerId, game)) {
if (flashbackAbility.canActivate(playerId, game).canActivate()) {
useable.put(flashbackAbility.getId(), flashbackAbility);
}
// Right Half
@ -1452,7 +1456,7 @@ public abstract class PlayerImpl implements Player, Serializable {
flashbackAbility.setControllerId(card.getOwnerId());
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_RIGHT);
flashbackAbility.setAbilityName(((SplitCard) card).getRightHalfCard().getName());
if (flashbackAbility.canActivate(playerId, game)) {
if (flashbackAbility.canActivate(playerId, game).canActivate()) {
useable.put(flashbackAbility.getId(), flashbackAbility);
}
@ -1469,7 +1473,7 @@ public abstract class PlayerImpl implements Player, Serializable {
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
for (ActivatedManaAbilityImpl ability : object.getAbilities().getActivatedManaAbilities(zone)) {
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
if (ability.canActivate(playerId, game)) {
if (ability.canActivate(playerId, game).canActivate()) {
useable.put(ability.getId(), ability);
}
}
@ -2477,7 +2481,7 @@ public abstract class PlayerImpl implements Player, Serializable {
Card card = game.getCard(entry.getKey());
if (card != null) {
// TODO: fix costs (why is Panglacial Wurm automatically accepting payment?)
player.cast(card.getSpellAbility(), game, false);
player.cast(card.getSpellAbility(), game, false, null);
}
chooseCard.clearChoice();
libraryCastableCardTracker.clear();
@ -2633,7 +2637,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (canUse == null) {
canUse = permanent.canUseActivatedAbilities(game);
}
if (canUse && ability.canActivate(playerId, game)) {
if (canUse && ability.canActivate(playerId, game).canActivate()) {
canAdd = true;
if (!ability.getManaCosts().isEmpty()) {
withCost = true;
@ -2677,7 +2681,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (canUse == null) {
canUse = permanent.canUseActivatedAbilities(game);
}
if (canUse && ability.canActivate(playerId, game)) {
if (canUse && ability.canActivate(playerId, game).canActivate()) {
canAdd = true;
}
}
@ -2692,7 +2696,7 @@ public abstract class PlayerImpl implements Player, Serializable {
canAdd = false;
break;
}
if (ability.canActivate(playerId, game)) {
if (ability.canActivate(playerId, game).canActivate()) {
canAdd = true;
}
}
@ -2712,7 +2716,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (canUse == null) {
canUse = permanent.canUseActivatedAbilities(game);
}
if (canUse && ability.canActivate(playerId, game) && !ability.getManaCosts().isEmpty()) {
if (canUse && ability.canActivate(playerId, game).canActivate() && !ability.getManaCosts().isEmpty()) {
result.add(permanent);
break;
}
@ -2732,7 +2736,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (!(ability instanceof ActivatedManaAbilityImpl)) {
ActivatedAbility copy = ability.copy();
copy.setCheckPlayableMode(); // prevents from endless loops for asking player to use effects by checking this mode
if (!copy.canActivate(playerId, game)) {
if (!copy.canActivate(playerId, game).canActivate()) {
return false;
}
if (available != null) {
@ -2765,10 +2769,10 @@ public abstract class PlayerImpl implements Player, Serializable {
if (available == null) {
return true;
}
UUID spendAnyManaSourceId = game.getContinuousEffects().asThough(ability.getSourceId(), AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game);
MageObjectReference permittingObject = game.getContinuousEffects().asThough(ability.getSourceId(), AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game);
for (Mana mana : abilityOptions) {
for (Mana avail : available) {
if (spendAnyManaSourceId != null && mana.count() <= avail.count()) {
if (permittingObject != null && mana.count() <= avail.count()) {
return true;
}
if (mana.enough(avail)) { // here we need to check if spend mana as though allow to pay the mana cost
@ -2893,13 +2897,13 @@ public abstract class PlayerImpl implements Player, Serializable {
}
private void getPlayableFromGraveyardCard(Game game, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
UUID asThoughtCastSourceId = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game);
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game);
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
boolean possible = false;
if (ability.getZone().match(Zone.GRAVEYARD)) {
possible = true;
} else if (ability.getZone().match(Zone.HAND) && (ability instanceof SpellAbility || ability instanceof PlayLandAbility)) {
if (asThoughtCastSourceId != null || canPlayCardsFromGraveyard()) {
if (permittingObject != null || canPlayCardsFromGraveyard()) {
possible = true;
}
}
@ -2968,12 +2972,12 @@ public abstract class PlayerImpl implements Player, Serializable {
}
for (ExileZone exile : game.getExile().getExileZones()) {
for (Card card : exile.getCards(game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
for (Ability ability : card.getAbilities()) {
if (ability.getZone().match(Zone.HAND)) {
ability.setControllerId(this.getId()); // controller must be set for case owner != caster
if (ability instanceof ActivatedAbility) {
if (((ActivatedAbility) ability).canActivate(playerId, game)) {
if (((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
playable.add(ability);
}
}
@ -2986,7 +2990,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// Check to play revealed cards
for (Cards cards : game.getState().getRevealed().values()) {
for (Card card : cards.getCards(game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
playable.add(ability);
@ -3001,7 +3005,7 @@ public abstract class PlayerImpl implements Player, Serializable {
if (player != null) {
if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) {
Card card = player.getLibrary().getFromTop(game);
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, getId(), game)) {
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
playable.add(ability);
@ -3354,7 +3358,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean lookAtFaceDownCard(Card card, Game game
) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.LOOK_AT_FACE_DOWN, this.getId(), game)) {
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.LOOK_AT_FACE_DOWN, card.getSpellAbility(), this.getId(), game)) {
if (chooseUse(Outcome.Benefit, "Look at that card?", null, game)) {
Cards cards = new CardsImpl(card);
this.lookAtCards(getName() + " - " + sdf.format(System.currentTimeMillis()), cards, game);