[LTR] Implement Witch-king of Angmar (#10563)

* Add card

* Add TapSourceEffect

* De-duplicate watcher logic

* Abstract and fix(?) logic

* Fix sacrifice targets

* Controller instead of Owner

* Add tests, fix, and refactor

* Throw if controller not supported

* Fix test (not supposed to start tapped)
This commit is contained in:
Bobby McCann 2023-07-06 00:22:21 +01:00 committed by GitHub
parent 008662be5e
commit c8564efbb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 202 additions and 28 deletions

View file

@ -27,7 +27,8 @@ public enum TargetController {
EACH_PLAYER,
ENCHANTED,
SOURCE_TARGETS,
MONARCH;
MONARCH,
SOURCE_CONTROLLER;
private final OwnerPredicate ownerPredicate;
private final PlayerPredicate playerPredicate;
@ -78,6 +79,8 @@ public enum TargetController {
case ENCHANTED:
Permanent permanent = input.getSource().getSourcePermanentIfItStillExists(game);
return permanent != null && input.getObject().isOwnedBy(permanent.getAttachedTo());
case SOURCE_CONTROLLER:
return card.isOwnedBy(input.getSource().getControllerId());
case SOURCE_TARGETS:
return card.isOwnedBy(input.getSource().getFirstTarget());
case MONARCH:
@ -119,8 +122,10 @@ public enum TargetController {
game.getPlayer(playerId).hasOpponent(player.getId(), game);
case NOT_YOU:
return !player.getId().equals(playerId);
case SOURCE_CONTROLLER:
return player.getId().equals(input.getSource().getControllerId());
case SOURCE_TARGETS:
return player.equals(input.getSource().getFirstTarget());
return player.getId().equals(input.getSource().getFirstTarget());
case MONARCH:
return player.getId().equals(game.getMonarchId());
default:
@ -162,6 +167,8 @@ public enum TargetController {
case ENCHANTED:
Permanent permanent = input.getSource().getSourcePermanentIfItStillExists(game);
return permanent != null && input.getObject().isControlledBy(permanent.getAttachedTo());
case SOURCE_CONTROLLER:
return object.isControlledBy(input.getSource().getControllerId());
case SOURCE_TARGETS:
return object.isControlledBy(input.getSource().getFirstTarget());
case MONARCH:

View file

@ -10,61 +10,83 @@ import mage.watchers.common.PlayerDamagedBySourceWatcher;
import java.util.UUID;
/**
* For use in abilities with this predicate:
* "_ that dealt (combat) damage to _ this turn"
*
* @author LevelX2
*/
public class DamagedPlayerThisTurnPredicate implements ObjectSourcePlayerPredicate<Controllable> {
private final TargetController controller;
private final TargetController playerDamaged;
public DamagedPlayerThisTurnPredicate(TargetController controller) {
this.controller = controller;
private final boolean combatDamageOnly;
public DamagedPlayerThisTurnPredicate(TargetController playerDamaged) {
this(playerDamaged, false);
}
public DamagedPlayerThisTurnPredicate(TargetController playerDamaged, boolean combatDamageOnly) {
this.playerDamaged = playerDamaged;
this.combatDamageOnly = combatDamageOnly;
}
@Override
public boolean apply(ObjectSourcePlayer<Controllable> input, Game game) {
Controllable object = input.getObject();
UUID objectId = input.getObject().getId();
UUID playerId = input.getPlayerId();
switch (controller) {
switch (playerDamaged) {
case YOU:
PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, playerId);
if (watcher != null) {
return watcher.hasSourceDoneDamage(object.getId(), game);
}
break;
// that dealt damage to you this turn
return playerDealtDamageBy(playerId, objectId, game);
case SOURCE_CONTROLLER:
// that dealt damage to this spell/ability's controller this turn
UUID controllerId = input.getSource().getControllerId();
return playerDealtDamageBy(controllerId, objectId, game);
case OPPONENT:
// that dealt damage to an opponent this turn
for (UUID opponentId : game.getOpponents(playerId)) {
watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, opponentId);
if (watcher != null) {
return watcher.hasSourceDoneDamage(object.getId(), game);
if (playerDealtDamageBy(opponentId, objectId, game)) {
return true;
}
}
break;
return false;
case NOT_YOU:
// that dealt damage to another player this turn
for (UUID notYouId : game.getState().getPlayersInRange(playerId, game)) {
if (!notYouId.equals(playerId)) {
watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, notYouId);
if (watcher != null) {
return watcher.hasSourceDoneDamage(object.getId(), game);
if (playerDealtDamageBy(notYouId, objectId, game)) {
return true;
}
}
}
break;
return false;
case ANY:
// that dealt damage to a player this turn
for (UUID anyId : game.getState().getPlayersInRange(playerId, game)) {
watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, anyId);
if (watcher != null) {
return watcher.hasSourceDoneDamage(object.getId(), game);
if (playerDealtDamageBy(anyId, objectId, game)) {
return true;
}
}
return true;
return false;
default:
throw new UnsupportedOperationException("TargetController not supported");
}
}
return false;
private boolean playerDealtDamageBy(UUID playerId, UUID objectId, Game game) {
PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, playerId);
if (watcher == null) {
return false;
}
if (combatDamageOnly) {
return watcher.hasSourceDoneCombatDamage(objectId, game);
}
return watcher.hasSourceDoneDamage(objectId, game);
}
@Override
public String toString() {
return "Damaged player (" + controller.toString() + ')';
return "Damaged player (" + playerDamaged.toString() + ')';
}
}

View file

@ -6,8 +6,8 @@ import java.util.Set;
import java.util.UUID;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.util.CardUtil;
import mage.watchers.Watcher;
@ -19,6 +19,7 @@ import mage.watchers.Watcher;
public class PlayerDamagedBySourceWatcher extends Watcher {
private final Set<String> damageSourceIds = new HashSet<>();
private final Set<String> combatDamageSourceIds = new HashSet<>();
public PlayerDamagedBySourceWatcher() {
super(WatcherScope.PLAYER);
@ -28,7 +29,11 @@ public class PlayerDamagedBySourceWatcher extends Watcher {
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
if (event.getTargetId().equals(controllerId)) {
damageSourceIds.add(CardUtil.getCardZoneString(null, event.getSourceId(), game));
String sourceId = CardUtil.getCardZoneString(null, event.getSourceId(), game);
damageSourceIds.add(sourceId);
if (((DamagedEvent) event).isCombatDamage()) {
combatDamageSourceIds.add(sourceId);
}
}
}
}
@ -45,6 +50,10 @@ public class PlayerDamagedBySourceWatcher extends Watcher {
return damageSourceIds.contains(CardUtil.getCardZoneString(null, sourceId, game));
}
public boolean hasSourceDoneCombatDamage(UUID sourceId, Game game) {
return combatDamageSourceIds.contains(CardUtil.getCardZoneString(null, sourceId, game));
}
@Override
public void reset() {
super.reset();