diff --git a/Mage.Sets/src/mage/cards/g/GollumObsessedStalker.java b/Mage.Sets/src/mage/cards/g/GollumObsessedStalker.java new file mode 100644 index 00000000000..e2c0a84b7c2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GollumObsessedStalker.java @@ -0,0 +1,197 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; +import mage.abilities.dynamicvalue.common.ControllerGainedLifeCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.SkulkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.watchers.Watcher; +import mage.watchers.common.PlayerGainedLifeWatcher; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Susucr + */ +public final class GollumObsessedStalker extends CardImpl { + + public GollumObsessedStalker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HALFLING); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Skulk + this.addAbility(new SkulkAbility()); + + // At the beginning of your end step, each opponent dealt combat damage this game by a creature named Gollum, Obsessed Stalker loses life equal to the amount of life you gained this turn. + Ability ability = new BeginningOfYourEndStepTriggeredAbility( + new GollumObsessedStalkerEffect(), + false + ); + ability.addWatcher(new PlayerGainedLifeWatcher()); + ability.addWatcher(new GollumObsessedStalkerWatcher()); + ability.addHint(ControllerGainedLifeCount.getHint()); + ability.addHint(GollumObsessedStalkerHint.instance); + + this.addAbility(ability); + } + + private GollumObsessedStalker(final GollumObsessedStalker card) { + super(card); + } + + @Override + public GollumObsessedStalker copy() { + return new GollumObsessedStalker(this); + } +} + +class GollumObsessedStalkerWatcher extends Watcher { + + // For each creature name, the players damaged by them during combat. + private final Map> playersPerName = new HashMap<>(); + + public GollumObsessedStalkerWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER + || !((DamagedPlayerEvent) event).isCombatDamage()) { + return; + } + Permanent creature = game.getPermanent(event.getSourceId()); + if (creature == null) { + return; + } + + String name = creature.getName(); + UUID playerId = event.getPlayerId(); + if (creature.getName().isEmpty() || playerId == null) { + return; + } + + playersPerName.computeIfAbsent(name, k -> new HashSet()); + playersPerName.get(name).add(playerId); + } + + public Set getPlayersDamagedByNamed(String name) { + return playersPerName.getOrDefault(name, new HashSet<>()); + } +} + +class GollumObsessedStalkerEffect extends OneShotEffect { + + GollumObsessedStalkerEffect() { + super(Outcome.LoseLife); + staticText = "each opponent dealt combat damage this game by a creature named " + + "{this} loses life equal to the amount of life you gained this turn."; + } + + private GollumObsessedStalkerEffect(final GollumObsessedStalkerEffect effect) { + super(effect); + } + + @Override + public GollumObsessedStalkerEffect copy() { + return new GollumObsessedStalkerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + GollumObsessedStalkerWatcher damageWatcher = game.getState().getWatcher(GollumObsessedStalkerWatcher.class); + PlayerGainedLifeWatcher lifeWatcher = game.getState().getWatcher(PlayerGainedLifeWatcher.class); + Permanent gollum = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (damageWatcher == null || lifeWatcher == null || gollum == null) { + return false; + } + + String name = gollum.getName(); + int amount = lifeWatcher.getLifeGained(source.getControllerId()); + Set playersDamaged = damageWatcher.getPlayersDamagedByNamed(name); + + if (amount == 0 || playersDamaged.isEmpty()) { + return true; + } + + for (UUID playerId : game.getOpponents(source.getControllerId())) { + if (!playersDamaged.contains(playerId)) { + continue; + } + + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + + player.loseLife(amount, game, source, false); + } + + return true; + } +} + +enum GollumObsessedStalkerHint implements Hint { + instance; + + @Override + public String getText(Game game, Ability ability) { + GollumObsessedStalkerWatcher watcher = game.getState().getWatcher(GollumObsessedStalkerWatcher.class); + if (watcher == null) { + return ""; + } + + String name = null; + Permanent gollum = game.getPermanentOrLKIBattlefield(ability.getSourceId()); + if (gollum != null) { + // Gollum is or was in play, its name is using LKI. + name = gollum.getName(); + } else { + // if Gollum LKI not in play (like in hand or in command zone), + // find the object. + MageObject gollumObj = game.getObject(ability.getSourceId()); + if (gollumObj != null) { + name = gollumObj.getName(); + } + } + if (name.isEmpty()) { + return ""; + } + + // Not filtering by opponent intentionally, just to provide full info everywhere. + List namesOfPlayersDealtDamage = + watcher.getPlayersDamagedByNamed(name) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getName) + .filter(n -> !n.isEmpty()) + .collect(Collectors.toList()); + + return "Players dealt combat damage by creatures named " + name + " this game: [" + + String.join(", ", namesOfPlayersDealtDamage) + + "]"; + } + + @Override + public GollumObsessedStalkerHint copy() { + return instance; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TalesOfMiddleEarthCommander.java b/Mage.Sets/src/mage/sets/TalesOfMiddleEarthCommander.java index 85a585ef983..3e122c36e40 100644 --- a/Mage.Sets/src/mage/sets/TalesOfMiddleEarthCommander.java +++ b/Mage.Sets/src/mage/sets/TalesOfMiddleEarthCommander.java @@ -126,6 +126,7 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Go for the Throat", 201, Rarity.UNCOMMON, mage.cards.g.GoForTheThroat.class)); cards.add(new SetCardInfo("Goblin Cratermaker", 218, Rarity.UNCOMMON, mage.cards.g.GoblinCratermaker.class)); cards.add(new SetCardInfo("Goblin Dark-Dwellers", 219, Rarity.RARE, mage.cards.g.GoblinDarkDwellers.class)); + cards.add(new SetCardInfo("Gollum, Obsessed Stalker", 26, Rarity.RARE, mage.cards.g.GollumObsessedStalker.class)); cards.add(new SetCardInfo("Graypelt Refuge", 316, Rarity.UNCOMMON, mage.cards.g.GraypeltRefuge.class)); cards.add(new SetCardInfo("Great Oak Guardian", 247, Rarity.UNCOMMON, mage.cards.g.GreatOakGuardian.class)); cards.add(new SetCardInfo("Grey Host Reinforcements", 14, Rarity.RARE, mage.cards.g.GreyHostReinforcements.class, NON_FULL_USE_VARIOUS));