mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
refactor: removed some usages of short LKI, moved static ability's useable zone logic to basic ability implementation;
This commit is contained in:
parent
52ebba4cd1
commit
740a9347ae
7 changed files with 32 additions and 30 deletions
|
|
@ -114,7 +114,7 @@ class VesuvanShapeshifterEffect extends OneShotEffect {
|
||||||
if (copyFromCreature != null) {
|
if (copyFromCreature != null) {
|
||||||
game.copyPermanent(Duration.Custom, copyFromCreature, copyToCreature.getId(), source, new VesuvanShapeShifterFaceUpCopyApplier());
|
game.copyPermanent(Duration.Custom, copyFromCreature, copyToCreature.getId(), source, new VesuvanShapeShifterFaceUpCopyApplier());
|
||||||
source.getTargets().clear();
|
source.getTargets().clear();
|
||||||
game.processAction(); // needed to get effects ready if copy happens in replacment and the copied abilities react of the same event (e.g. turn face up)
|
game.processAction(); // needed to get effects ready if copy happens in replacement and the copied abilities react of the same event (e.g. turn face up)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import org.mage.test.serverside.base.CardTestCommanderDuelBase;
|
||||||
*
|
*
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class CommanderManaReplacmentTest extends CardTestCommanderDuelBase {
|
public class CommanderManaReplacementTest extends CardTestCommanderDuelBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||||
|
|
@ -357,6 +357,8 @@ public interface Ability extends Controllable, Serializable {
|
||||||
* - for normal abilities and triggers - keep default
|
* - for normal abilities and triggers - keep default
|
||||||
* - for leave battlefield triggers - keep default + set setLeavesTheBattlefieldTrigger(true)
|
* - for leave battlefield triggers - keep default + set setLeavesTheBattlefieldTrigger(true)
|
||||||
* - for dies triggers - override and use TriggeredAbilityImpl.isInUseableZoneDiesTrigger inside + set setLeavesTheBattlefieldTrigger(true)
|
* - for dies triggers - override and use TriggeredAbilityImpl.isInUseableZoneDiesTrigger inside + set setLeavesTheBattlefieldTrigger(true)
|
||||||
|
*
|
||||||
|
* @param source can be null for static continues effects checking like rules modification (example: Yixlid Jailer)
|
||||||
*/
|
*/
|
||||||
boolean isInUseableZone(Game game, MageObject source, GameEvent event);
|
boolean isInUseableZone(Game game, MageObject source, GameEvent event);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1179,6 +1179,8 @@ public abstract class AbilityImpl implements Ability {
|
||||||
if (!this.hasSourceObjectAbility(game, source, event)) {
|
if (!this.hasSourceObjectAbility(game, source, event)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in command zone
|
||||||
if (zone == Zone.COMMAND) {
|
if (zone == Zone.COMMAND) {
|
||||||
if (this.getSourceId() == null) { // commander effects
|
if (this.getSourceId() == null) { // commander effects
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1199,20 +1201,18 @@ public abstract class AbilityImpl implements Ability {
|
||||||
parameterSourceId = getSourceId();
|
parameterSourceId = getSourceId();
|
||||||
}
|
}
|
||||||
|
|
||||||
// old code:
|
// on entering permanents - must use static abilities like it already on battlefield
|
||||||
// TODO: delete after dies fix
|
// example: Tatterkite enters without counters from Mikaeus, the Unhallowed
|
||||||
// check against shortLKI for effects that move multiple object at the same time (e.g. destroy all)
|
if (game.getPermanentEntering(parameterSourceId) != null && zone == Zone.BATTLEFIELD) {
|
||||||
if (game.checkShortLivingLKI(getSourceId(), getZone())) {
|
return true;
|
||||||
//return true; // fix 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 603.10.
|
// 603.10.
|
||||||
// Normally, objects that exist immediately after an event are checked to see if the event matched
|
// Normally, objects that exist immediately after an event are checked to see if the event matched
|
||||||
// any trigger conditions, and continuous effects that exist at that time are used to determine what the
|
// any trigger conditions, and continuous effects that exist at that time are used to determine what the
|
||||||
// trigger conditions are and what the objects involved in the event look like.
|
// trigger conditions are and what the objects involved in the event look like.
|
||||||
// ...
|
// ...
|
||||||
Zone lookingInZone = game.getState().getZone(parameterSourceId);
|
Zone sourceObjectZone = game.getState().getZone(parameterSourceId);
|
||||||
|
|
||||||
// 603.10.
|
// 603.10.
|
||||||
// ...
|
// ...
|
||||||
|
|
@ -1226,9 +1226,16 @@ public abstract class AbilityImpl implements Ability {
|
||||||
// players can see is put into a hand or library.
|
// players can see is put into a hand or library.
|
||||||
// TODO: research "leaves a graveyard"
|
// TODO: research "leaves a graveyard"
|
||||||
// TODO: research "put into a hand or library"
|
// TODO: research "put into a hand or library"
|
||||||
if (source instanceof Permanent && isTriggerCanFireAfterLeaveBattlefield(event)) {
|
if (isTriggerCanFireAfterLeaveBattlefield(event)) {
|
||||||
// support leaves-the-battlefield abilities
|
// permanents with normal triggers
|
||||||
lookingInZone = Zone.BATTLEFIELD;
|
if (source instanceof Permanent) {
|
||||||
|
// support leaves-the-battlefield abilities
|
||||||
|
sourceObjectZone = Zone.BATTLEFIELD;
|
||||||
|
}
|
||||||
|
// permanents with continues effects like Yixlid Jailer, see related code "isInUseableZone(game, null"
|
||||||
|
if (source == null && this instanceof StaticAbility) {
|
||||||
|
sourceObjectZone = Zone.BATTLEFIELD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: research use cases and implement shared logic with "looking zone" instead LKI only
|
// TODO: research use cases and implement shared logic with "looking zone" instead LKI only
|
||||||
|
|
@ -1239,7 +1246,7 @@ public abstract class AbilityImpl implements Ability {
|
||||||
// 603.10f Abilities that trigger when a player loses the game look back in time.
|
// 603.10f Abilities that trigger when a player loses the game look back in time.
|
||||||
// 603.10g Abilities that trigger when a player planeswalks away from a plane look back in time.
|
// 603.10g Abilities that trigger when a player planeswalks away from a plane look back in time.
|
||||||
|
|
||||||
return zone.match(lookingInZone);
|
return zone.match(sourceObjectZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isTriggerCanFireAfterLeaveBattlefield(GameEvent event) {
|
public static boolean isTriggerCanFireAfterLeaveBattlefield(GameEvent event) {
|
||||||
|
|
@ -1255,6 +1262,7 @@ public abstract class AbilityImpl implements Ability {
|
||||||
}
|
}
|
||||||
|
|
||||||
return allEvents.stream().anyMatch(e -> {
|
return allEvents.stream().anyMatch(e -> {
|
||||||
|
// TODO: need sync code with TriggeredAbilityImpl.isInUseableZone
|
||||||
// TODO: add more events with zone change logic (or make it event's param)?
|
// TODO: add more events with zone change logic (or make it event's param)?
|
||||||
// need research: is it ability's or event's task?
|
// need research: is it ability's or event's task?
|
||||||
// - ability's task: code like ability.setLookBackInTime
|
// - ability's task: code like ability.setLookBackInTime
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
|
|
||||||
package mage.abilities;
|
package mage.abilities;
|
||||||
|
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.constants.AbilityType;
|
import mage.constants.AbilityType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
|
||||||
import mage.game.events.GameEvent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -25,17 +21,6 @@ public abstract class StaticAbility extends AbilityImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
|
|
||||||
if (game.checkShortLivingLKI(getSourceId(), zone)) { // TODO: can be deleted? Need research
|
|
||||||
return true; // maybe this can be a problem if effects removed the ability from the object
|
|
||||||
}
|
|
||||||
if (game.getPermanentEntering(getSourceId()) != null && zone == Zone.BATTLEFIELD) {
|
|
||||||
return true; // abilities of permanents entering battlefield are countes as on battlefield
|
|
||||||
}
|
|
||||||
return super.isInUseableZone(game, source, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected StaticAbility(final StaticAbility ability) {
|
protected StaticAbility(final StaticAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -376,11 +376,16 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
||||||
* Kozilek card is itself and has the ability.
|
* Kozilek card is itself and has the ability.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// process events from other objects
|
||||||
Set<UUID> eventTargets = CardUtil.getEventTargets(event);
|
Set<UUID> eventTargets = CardUtil.getEventTargets(event);
|
||||||
if (!eventTargets.contains(getSourceId())) {
|
if (!eventTargets.contains(getSourceId())) {
|
||||||
return super.isInUseableZone(game, source, event);
|
return super.isInUseableZone(game, source, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process events from own object
|
||||||
|
|
||||||
|
// inject process of "look back in time" events
|
||||||
|
// TODO: need sync code with AbilityImpl.isInUseableZone
|
||||||
switch (event.getType()) {
|
switch (event.getType()) {
|
||||||
case ZONE_CHANGE:
|
case ZONE_CHANGE:
|
||||||
ZoneChangeEvent zce = (ZoneChangeEvent) event;
|
ZoneChangeEvent zce = (ZoneChangeEvent) event;
|
||||||
|
|
@ -405,6 +410,8 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all other events from own object
|
||||||
return super.isInUseableZone(game, source, event);
|
return super.isInUseableZone(game, source, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
if (effect instanceof PayCostToAttackBlockEffect) {
|
if (effect instanceof PayCostToAttackBlockEffect) {
|
||||||
Set<Ability> abilities = replacementEffects.getAbility(effect.getId());
|
Set<Ability> abilities = replacementEffects.getAbility(effect.getId());
|
||||||
for (Ability ability : abilities) {
|
for (Ability ability : abilities) {
|
||||||
// for replacment effects of static abilities do not use LKI to check if to apply
|
// for replacement effects of static abilities do not use LKI to check if to apply
|
||||||
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
|
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
|
||||||
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
|
||||||
if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) {
|
if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) {
|
||||||
|
|
@ -370,7 +370,7 @@ public class ContinuousEffects implements Serializable {
|
||||||
Set<Ability> abilities = replacementEffects.getAbility(effect.getId());
|
Set<Ability> abilities = replacementEffects.getAbility(effect.getId());
|
||||||
Set<Ability> applicableAbilities = new HashSet<>();
|
Set<Ability> applicableAbilities = new HashSet<>();
|
||||||
for (Ability ability : abilities) {
|
for (Ability ability : abilities) {
|
||||||
// for replacment effects of static abilities do not use LKI to check if to apply
|
// for replacement effects of static abilities do not use LKI to check if to apply
|
||||||
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
|
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
|
||||||
if (!effect.isUsed()) {
|
if (!effect.isUsed()) {
|
||||||
if (!game.getScopeRelevant()
|
if (!game.getScopeRelevant()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue