Skip to content

Commit

Permalink
multiple changes:
Browse files Browse the repository at this point in the history
* refactor: improved target pointer init code and logic, added docs and runtime checks;
* game: fixed miss or wrong init calls in some continuous effects;
* game: fixed wrong usage of target pointers (miss copy code, miss npe checks);
JayDi85 committed Feb 18, 2024
1 parent b2aa4ec commit 78612dd
Showing 115 changed files with 463 additions and 352 deletions.
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/a/AuriokReplica.java
Original file line number Diff line number Diff line change
@@ -64,8 +64,8 @@ public AuriokReplicaEffect copy() {

@Override
public void init(Ability source, Game game) {
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
super.init(source, game);
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
}

@Override
6 changes: 4 additions & 2 deletions Mage.Sets/src/mage/cards/b/BackdraftHellkite.java
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
import mage.game.Game;
import mage.players.Player;

import java.util.Objects;
import java.util.UUID;

/**
@@ -74,8 +75,9 @@ public void init(Ability source, Game game) {
}
player.getGraveyard()
.stream()
.map((cardId) -> game.getCard(cardId))
.filter(card1 -> card1.isInstantOrSorcery(game))
.map(game::getCard)
.filter(Objects::nonNull)
.filter(card -> card.isInstantOrSorcery(game))
.forEachOrdered(card -> affectedObjectList.add(new MageObjectReference(card, game)));
}

2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/b/BeaconOfDestiny.java
Original file line number Diff line number Diff line change
@@ -71,8 +71,8 @@ public BeaconOfDestinyEffect copy() {

@Override
public void init(Ability source, Game game) {
this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
super.init(source, game);
this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
}

@Override
10 changes: 5 additions & 5 deletions Mage.Sets/src/mage/cards/b/Besmirch.java
Original file line number Diff line number Diff line change
@@ -61,22 +61,22 @@ public BesmirchEffect copy() {
@Override
public boolean apply(Game game, Ability source) {
if (game.getPermanent(source.getFirstTarget()) != null) {
TargetPointer target = new FixedTarget(source.getFirstTarget(), game);
TargetPointer blueprintTarget = new FixedTarget(source.getFirstTarget(), game);

// gain control
game.addEffect(new GainControlTargetEffect(Duration.EndOfTurn)
.setTargetPointer(target), source);
.setTargetPointer(blueprintTarget.copy()), source);

// haste
game.addEffect(new GainAbilityTargetEffect(
HasteAbility.getInstance(), Duration.EndOfTurn
).setTargetPointer(target), source);
).setTargetPointer(blueprintTarget.copy()), source);

// goad
game.addEffect(new GoadTargetEffect().setTargetPointer(target), source);
game.addEffect(new GoadTargetEffect().setTargetPointer(blueprintTarget.copy()), source);

// untap
new UntapTargetEffect().setTargetPointer(target).apply(game, source);
new UntapTargetEffect().setTargetPointer(blueprintTarget.copy()).apply(game, source);

return true;
}
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/b/BoneMask.java
Original file line number Diff line number Diff line change
@@ -68,8 +68,8 @@ public BoneMaskEffect copy() {

@Override
public void init(Ability source, Game game) {
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
super.init(source, game);
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
}

@Override
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java
Original file line number Diff line number Diff line change
@@ -74,6 +74,7 @@ public ChoArrimAlchemistEffect copy() {

@Override
public void init(Ability source, Game game) {
super.init(source, game);
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
}

4 changes: 3 additions & 1 deletion Mage.Sets/src/mage/cards/c/CommandersPlate.java
Original file line number Diff line number Diff line change
@@ -86,15 +86,17 @@ public CommandersPlateEffect copy() {

@Override
public void init(Ability source, Game game) {
super.init(source, game);
if (!affectedObjectsSet) {
return;
}
Permanent equipment = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (equipment == null || equipment.getAttachedTo() == null) {
discard();
return;
}
this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game));

super.init(source, game); // must call at the end due target pointer setup
}

@Override
5 changes: 5 additions & 0 deletions Mage.Sets/src/mage/cards/c/ConvincingMirage.java
Original file line number Diff line number Diff line change
@@ -71,6 +71,11 @@ public ConvincingMirageContinousEffect copy() {
public void init(Ability source, Game game) {
super.init(source, game);
SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY));
if (choice == null) {
discard();
return;
}

switch (choice) {
case FOREST:
dependencyTypes.add(DependencyType.BecomeForest);
6 changes: 3 additions & 3 deletions Mage.Sets/src/mage/cards/c/CorpseDance.java
Original file line number Diff line number Diff line change
@@ -79,14 +79,14 @@ public boolean apply(Game game, Ability source) {
if (controller.moveCards(lastCreatureCard, Zone.BATTLEFIELD, source, game)) {
Permanent creature = game.getPermanent(lastCreatureCard.getId());
if (creature != null) {
FixedTarget fixedTarget = new FixedTarget(creature, game);
FixedTarget blueprintTarget = new FixedTarget(creature, game);
// Gains Haste
ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn);
hasteEffect.setTargetPointer(fixedTarget);
hasteEffect.setTargetPointer(blueprintTarget.copy());
game.addEffect(hasteEffect, source);
// Exile it at end of turn
ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD);
exileEffect.setTargetPointer(fixedTarget);
exileEffect.setTargetPointer(blueprintTarget.copy());
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);
game.addDelayedTriggeredAbility(delayedAbility, source);
}
6 changes: 3 additions & 3 deletions Mage.Sets/src/mage/cards/c/CosmiumConfluence.java
Original file line number Diff line number Diff line change
@@ -104,16 +104,16 @@ public boolean apply(Game game, Ability source) {
}

controller.choose(outcome, target, source, game);
FixedTarget fixedTarget = new FixedTarget(target.getFirstTarget(), game);
FixedTarget blueprintTarget = new FixedTarget(target.getFirstTarget(), game);
new AddCountersTargetEffect(CounterType.P1P1.createInstance(3))
.setTargetPointer(fixedTarget)
.setTargetPointer(blueprintTarget.copy())
.apply(game, source);
ContinuousEffect effect = new BecomesCreatureTargetEffect(
new CreatureToken(0, 0, "0/0 Elemental creature with haste", SubType.ELEMENTAL)
.withAbility(HasteAbility.getInstance()),
false, true, Duration.Custom
);
effect.setTargetPointer(fixedTarget);
effect.setTargetPointer(blueprintTarget.copy());
game.addEffect(effect, source);
return true;
}
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/cards/d/DarkSphere.java
Original file line number Diff line number Diff line change
@@ -70,6 +70,7 @@ public DarkSpherePreventionEffect copy() {

@Override
public void init(Ability source, Game game) {
super.init(source, game);
this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
}

2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/d/DeflectingPalm.java
Original file line number Diff line number Diff line change
@@ -62,8 +62,8 @@ public DeflectingPalmEffect copy() {

@Override
public void init(Ability source, Game game) {
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
super.init(source, game);
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
}

@Override
12 changes: 8 additions & 4 deletions Mage.Sets/src/mage/cards/d/DesperateGambit.java
Original file line number Diff line number Diff line change
@@ -65,12 +65,16 @@ private DesperateGambitEffect(final DesperateGambitEffect effect) {

@Override
public void init(Ability source, Game game) {
this.target.choose(Outcome.Benefit, source.getControllerId(), source.getSourceId(), source, game);
super.init(source, game);

Player you = game.getPlayer(source.getControllerId());
if(you != null) {
wonFlip = you.flipCoin(source, game, true);
super.init(source, game);
if (you == null) {
discard();
return;
}

this.target.choose(Outcome.Benefit, source.getControllerId(), source.getSourceId(), source, game);
this.wonFlip = you.flipCoin(source, game, true);
}

@Override
8 changes: 4 additions & 4 deletions Mage.Sets/src/mage/cards/d/DisplacedDinosaurs.java
Original file line number Diff line number Diff line change
@@ -78,15 +78,15 @@ public boolean applies(GameEvent event, Ability source, Game game) {
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent historic = ((EntersTheBattlefieldEvent) event).getTarget();
if (historic != null) {
TargetPointer historicTarget = new FixedTarget(historic.getId(), historic.getZoneChangeCounter(game) + 1);
TargetPointer blueprintTarget = new FixedTarget(historic.getId(), historic.getZoneChangeCounter(game) + 1);
ContinuousEffect creatureEffect = new AddCardTypeTargetEffect(Duration.Custom, CardType.CREATURE);
creatureEffect.setTargetPointer(historicTarget);
creatureEffect.setTargetPointer(blueprintTarget.copy());
game.addEffect(creatureEffect, source);
ContinuousEffect dinosaurEffect = new AddCardSubTypeTargetEffect(SubType.DINOSAUR, Duration.Custom);
dinosaurEffect.setTargetPointer(historicTarget);
dinosaurEffect.setTargetPointer(blueprintTarget.copy());
game.addEffect(dinosaurEffect, source);
ContinuousEffect sevenSevenEffect = new SetBasePowerToughnessTargetEffect(7, 7, Duration.Custom);
sevenSevenEffect.setTargetPointer(historicTarget);
sevenSevenEffect.setTargetPointer(blueprintTarget.copy());
game.addEffect(sevenSevenEffect, source);
}
return false;
8 changes: 5 additions & 3 deletions Mage.Sets/src/mage/cards/e/ElrondOfTheWhiteCouncil.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mage.cards.e;

import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
@@ -23,6 +24,7 @@
import mage.target.targetpointer.TargetPointer;
import mage.util.CardUtil;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@@ -127,16 +129,16 @@ public boolean apply(Game game, Ability source) {
}

// You gain control of each creature chosen this way, and they gain "This creature can't attack its owner."
TargetPointer pointer = new FixedTargets(chosenCreatures.stream().collect(Collectors.toList()), game);
TargetPointer blueprintTarget = new FixedTargets(new ArrayList<>(chosenCreatures), game);

game.addEffect(new GainControlTargetEffect(
Duration.WhileOnBattlefield
).setTargetPointer(pointer), source);
).setTargetPointer(blueprintTarget.copy()), source);

game.addEffect(new GainAbilityTargetEffect(
new SimpleStaticAbility(new CantAttackItsOwnerEffect()),
Duration.WhileOnBattlefield
).setTargetPointer(pointer), source);
).setTargetPointer(blueprintTarget.copy()), source);

// Need to process the control change.
game.getState().processAction(game);
5 changes: 5 additions & 0 deletions Mage.Sets/src/mage/cards/e/ElsewhereFlask.java
Original file line number Diff line number Diff line change
@@ -95,6 +95,11 @@ public ElsewhereFlaskContinuousEffect copy() {
public void init(Ability source, Game game) {
super.init(source, game);
SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "_ElsewhereFlask"));
if (choice == null) {
discard();
return;
}

switch (choice) {
case FOREST:
dependencyTypes.add(DependencyType.BecomeForest);
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/e/EyeForAnEye.java
Original file line number Diff line number Diff line change
@@ -59,8 +59,8 @@ public EyeForAnEyeEffect copy() {

@Override
public void init(Ability source, Game game) {
this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
super.init(source, game);
this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
}

@Override
6 changes: 3 additions & 3 deletions Mage.Sets/src/mage/cards/f/Fecundity.java
Original file line number Diff line number Diff line change
@@ -54,9 +54,9 @@ public FecundityEffect copy() {

@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer()
// Card can be moved again (e.g. commander replacement) so we need the row id from fixed target to check
.getFixedTarget(game, source).getTarget(), Zone.BATTLEFIELD);
// card can be moved again (e.g. commander replacement) so we need the row id from fixed target to check
// TODO: bugged with commander replacement effects?
Permanent permanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD);
if (permanent != null) {
Player controller = game.getPlayer(permanent.getControllerId());
if (controller != null) {
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/g/GeneralsRegalia.java
Original file line number Diff line number Diff line change
@@ -67,8 +67,8 @@ public GeneralsRegaliaEffect copy() {

@Override
public void init(Ability source, Game game) {
this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
super.init(source, game);
this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
}

@Override
37 changes: 19 additions & 18 deletions Mage.Sets/src/mage/cards/g/GiantOyster.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package mage.cards.g;

import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
@@ -28,8 +27,9 @@
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;

import java.util.UUID;

/**
*
* @author noahg
*/
public final class GiantOyster extends CardImpl {
@@ -103,23 +103,24 @@ public GiantOysterCreateDelayedTriggerEffects copy() {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent oyster = game.getPermanent(source.getSourceId());
Permanent tappedCreature = game.getPermanent(source.getFirstTarget());
if (oyster != null && tappedCreature != null) {
Effect addCountersEffect = new AddCountersTargetEffect(CounterType.M1M1.createInstance(1));
addCountersEffect.setTargetPointer(getTargetPointer().getFixedTarget(game, source));
DelayedTriggeredAbility drawStepAbility = new AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(addCountersEffect, Duration.Custom, false);
drawStepAbility.setControllerId(source.getControllerId());
UUID drawStepAbilityUUID = game.addDelayedTriggeredAbility(drawStepAbility, source);

DelayedTriggeredAbility leaveUntapDelayedTriggeredAbility = new GiantOysterLeaveUntapDelayedTriggeredAbility(drawStepAbilityUUID);
leaveUntapDelayedTriggeredAbility.getEffects().get(0).setTargetPointer(new FixedTarget(tappedCreature, game));
game.addDelayedTriggeredAbility(leaveUntapDelayedTriggeredAbility, source);
return true;
}
Permanent oyster = game.getPermanent(source.getSourceId());
Permanent tappedCreature = game.getPermanent(source.getFirstTarget());
FixedTarget fixedTarget = getTargetPointer().getFirstAsFixedTarget(game, source);
if (controller == null || oyster == null || tappedCreature == null || fixedTarget == null) {
return false;
}
return false;

Effect addCountersEffect = new AddCountersTargetEffect(CounterType.M1M1.createInstance(1));
addCountersEffect.setTargetPointer(fixedTarget);
DelayedTriggeredAbility drawStepAbility = new AtTheBeginOfYourNextDrawStepDelayedTriggeredAbility(addCountersEffect, Duration.Custom, false);
drawStepAbility.setControllerId(source.getControllerId());
UUID drawStepAbilityUUID = game.addDelayedTriggeredAbility(drawStepAbility, source);

DelayedTriggeredAbility leaveUntapDelayedTriggeredAbility = new GiantOysterLeaveUntapDelayedTriggeredAbility(drawStepAbilityUUID);
leaveUntapDelayedTriggeredAbility.getEffects().get(0).setTargetPointer(new FixedTarget(tappedCreature, game));
game.addDelayedTriggeredAbility(leaveUntapDelayedTriggeredAbility, source);
return true;

}
}

3 changes: 2 additions & 1 deletion Mage.Sets/src/mage/cards/g/GiftOfFangs.java
Original file line number Diff line number Diff line change
@@ -66,7 +66,6 @@ public GiftOfFangsEffect copy() {

@Override
public void init(Ability source, Game game) {
super.init(source, game);
if (affectedObjectsSet) {
// Added boosts of activated or triggered abilities exist independent from the source they are created by
// so a continuous effect for the permanent itself with the attachment is created
@@ -75,6 +74,8 @@ public void init(Ability source, Game game) {
this.setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game.getState().getZoneChangeCounter(equipment.getAttachedTo())));
}
}

super.init(source, game); // must call at the end due target pointer setup
}

@Override
9 changes: 8 additions & 1 deletion Mage.Sets/src/mage/cards/g/GoblinPsychopath.java
Original file line number Diff line number Diff line change
@@ -59,8 +59,15 @@ private GoblinPsychopathEffect(final GoblinPsychopathEffect effect) {

@Override
public void init(Ability source, Game game) {
this.wonFlip = game.getPlayer(source.getControllerId()).flipCoin(source, game, true);
super.init(source, game);

Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
discard();
return;
}

this.wonFlip = controller.flipCoin(source, game, true);
}

@Override
Loading

0 comments on commit 78612dd

Please sign in to comment.